codeforces 863F Almost Permutation 费用流 好题!

来源:互联网 发布:移动免费流量软件 编辑:程序博客网 时间:2024/05/18 03:11

F. Almost Permutation
time limit per test
3 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

Recently Ivan noticed an array a while debugging his code. Now Ivan can't remember this array, but the bug he was trying to fix didn't go away, so Ivan thinks that the data from this array might help him to reproduce the bug.

Ivan clearly remembers that there were n elements in the array, and each element was not less than 1 and not greater than n. Also he remembers q facts about the array. There are two types of facts that Ivan remembers:

  • 1 li ri vi — for each x such that li ≤ x ≤ ri ax ≥ vi;
  • 2 li ri vi — for each x such that li ≤ x ≤ ri ax ≤ vi.

Also Ivan thinks that this array was a permutation, but he is not so sure about it. He wants to restore some array that corresponds to the qfacts that he remembers and is very similar to permutation. Formally, Ivan has denoted the cost of array as follows:

, where cnt(i) is the number of occurences of i in the array.

Help Ivan to determine minimum possible cost of the array that corresponds to the facts!

Input

The first line contains two integer numbers n and q (1 ≤ n ≤ 500 ≤ q ≤ 100).

Then q lines follow, each representing a fact about the array. i-th line contains the numbers tiliri and vi for i-th fact (1 ≤ ti ≤ 21 ≤ li ≤ ri ≤ n1 ≤ vi ≤ nti denotes the type of the fact).

Output

If the facts are controversial and there is no array that corresponds to them, print -1. Otherwise, print minimum possible cost of the array.

Examples
input
3 0
output
3
input
3 11 1 3 2
output
5
input
3 21 1 3 22 1 3 2
output
9
input
3 21 1 3 22 1 3 1
output
-1
第一次写费用流的题目。

这道题的建图方法非常的巧妙。

我们根据给出的条件,确定每个元素所处于的区间范围,表明这个元素应该在这个区间[min[i],max[i]]内取值。

我们从源点0出发,向每个元素1,2,3...n连接一条容量为1,费用为0的边,表示选取这个元素。

然后从第i个元素向[min[i],max[i]]中的所有元素j连接一条容量为1,费用为0的边,代表给元素i赋值为j。

下面就是关键了:

从i+n 向 汇点2*n+1连接n条边,每条边的容量都是1,但是费用从1,3,5,。。。不等。

然后跑一边最小费用最大流就可以了。

为什么这么做是正确的呢?

我们想,假设有t个元素都流向的第i+n个节点,这意味着t个元素都赋值为了i。

当t = 1时,一定会选择费用为1的边流,total cost = 1=1*1

当t = 2时,一定会选择费用为1,3的边流,total cost = 1+3=2*2

当t = 3时,一定会选择费用为1,3,5的边流,total cost = 1+3+5=3*3

当t = 4时,一定会选择费用为1,3,5,7的边流,total cost = 1+3+5+7=4*4

在这里利用了平方数的一个性质,非常巧妙。

代码:

#include<bits/stdc++.h>using namespace std;const int inf = 1e9;const int mm = 111111;const int maxn = 999;int node,src,dest,edge;int ver[mm],flow[mm],cst[mm],nxt[mm];int head[maxn],work[maxn],dis[maxn],q[maxn];int tot_cost;void prepare(int _node,int _src,int _dest){    node=_node,src=_src,dest=_dest;    for(int i=0; i<node; ++i)head[i]=-1;    edge=0;    tot_cost = 0;}void add_edge(int u,int v,int c,int cost){    ver[edge]=v,flow[edge]=c,nxt[edge]=head[u],cst[edge]=cost,head[u]=edge++;    ver[edge]=u,flow[edge]=0,nxt[edge]=head[v],cst[edge]=-cost,head[v]=edge++;}/**广搜计算出每个点与源点的最短距离,如果不能到达汇点说明算法结束*/int ins[maxn];int pre[maxn];bool Dinic_spfa(){memset(ins,0,sizeof(ins));memset(dis,-1,sizeof(dis));memset(pre,-1,sizeof(pre));queue<int> Q;    //int i,u,v,l,r=0;    Q.push(src);    dis[src] = 0,ins[src] = 1;    pre[src] = -1;    while(!Q.empty()){    int u = Q.front();Q.pop();ins[u] = 0;for(int e = head[u];e != -1;e = nxt[e]){int v = ver[e];if(!flow[e]) continue;if(dis[v] < 0 || dis[v] > dis[u] + cst[e]){dis[v] = dis[u] + cst[e];pre[v] = e;if(!ins[v]) ins[v] = 1,Q.push(v);}}    }    return dis[dest] != -1;}int Dinic_flow(){    int i,ret=0,delta=inf;    while(Dinic_spfa())    {    for(int i=pre[dest];i != -1;i = pre[ver[i^1]])    delta = min(delta,flow[i]);for(int i=pre[dest];i != -1;i = pre[ver[i^1]])    flow[i] -= delta,flow[i^1] += delta;ret+=delta;tot_cost += dis[dest]*delta;    }    return ret;}int getint() {      int x = 0, flag = 0; char ch = getchar();    for(; !isdigit(ch); ch = getchar())         if (ch == '-') flag = 1;    for(; isdigit(ch); ch = getchar())        x = x * 10 + ch - '0';     return flag ? -x : x;}int n,m;int mi[100],ma[100];int main(){n = getint();m = getint();prepare(2*n+2,0,2*n+1);for(int i = 1;i <= n;++i){mi[i] = 1;ma[i] = n;}while(m--){int tp,l,r,v;tp = getint();l = getint();r = getint();v = getint();for(int i = l;i <= r;++i){if(tp == 1)mi[i] = max(mi[i],v);elsema[i] = min(ma[i],v);if(mi[i] > ma[i] || mi[i] < 1 || ma[i] > n) return 0*puts("-1");}} for(int i = 1;i <= n;i++) add_edge(0,i,1,0);for(int i = 1;i <= n;i++) for(int j = mi[i];j <= ma[i];++j)add_edge(i,j+n,1,0);for(int i = n+1;i <= 2*n;++i) for(int j = 0;j < n;++j)add_edge(i,2*n+1,1,2*j+1);int f = Dinic_flow();cout<<tot_cost<<endl;}

附费用流模板:

#include<bits/stdc++.h>using namespace std;const int inf = 1e9;const int mm = 111111;const int maxn = 999;int node,src,dest,edge;int ver[mm],flow[mm],cst[mm],nxt[mm];int head[maxn],work[maxn],dis[maxn],q[maxn];int tot_cost;void prepare(int _node,int _src,int _dest){    node=_node,src=_src,dest=_dest;    for(int i=0; i<node; ++i)head[i]=-1;    edge=0;    tot_cost = 0;}void add_edge(int u,int v,int c,int cost){    ver[edge]=v,flow[edge]=c,nxt[edge]=head[u],cst[edge]=cost,head[u]=edge++;    ver[edge]=u,flow[edge]=0,nxt[edge]=head[v],cst[edge]=-cost,head[v]=edge++;}/**广搜计算出每个点与源点的最短距离,如果不能到达汇点说明算法结束*/int ins[maxn];int pre[maxn];bool Dinic_spfa(){memset(ins,0,sizeof(ins));memset(dis,-1,sizeof(dis));memset(pre,-1,sizeof(pre));queue<int> Q;    //int i,u,v,l,r=0;    Q.push(src);    dis[src] = 0,ins[src] = 1;    pre[src] = -1;    while(!Q.empty()){    int u = Q.front();Q.pop();ins[u] = 0;for(int e = head[u];e != -1;e = nxt[e]){int v = ver[e];if(!flow[e]) continue;if(dis[v] < 0 || dis[v] > dis[u] + cst[e]){dis[v] = dis[u] + cst[e];pre[v] = e;if(!ins[v]) ins[v] = 1,Q.push(v);}}    }    return dis[dest] != -1;}int Dinic_flow(){    int i,ret=0,delta=inf;    while(Dinic_spfa())    {    for(int i=pre[dest];i != -1;i = pre[ver[i^1]])    delta = min(delta,flow[i]);for(int i=pre[dest];i != -1;i = pre[ver[i^1]])    flow[i] -= delta,flow[i^1] += delta;ret+=delta;tot_cost += dis[dest]*delta;    }    return ret;}int getint() {      int x = 0, flag = 0; char ch = getchar();    for(; !isdigit(ch); ch = getchar())         if (ch == '-') flag = 1;    for(; isdigit(ch); ch = getchar())        x = x * 10 + ch - '0';     return flag ? -x : x;}




阅读全文
0 0
原创粉丝点击