Problem C:【SCOI2011 Day1】糖果
Time Limit:10000MS Memory Limit:165536K
Total Submit:308 Accepted:74
Case Time Limit:3000MS
Description
幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww需要满足小朋友们的K个要求。幼儿园的糖果总是有限的,lxhgww想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
Input
输入的第一行是两个整数N,K。
接下来K行,表示这些点需要满足的关系,每行3个数字,X,A,B。
如果X=1, 表示第A个小朋友分到的糖果必须和第B个小朋友分到的糖果一样多;
如果X=2, 表示第A个小朋友分到的糖果必须少于第B个小朋友分到的糖果;
如果X=3, 表示第A个小朋友分到的糖果必须不少于第B个小朋友分到的糖果;
如果X=4, 表示第A个小朋友分到的糖果必须多于第B个小朋友分到的糖果;
如果X=5, 表示第A个小朋友分到的糖果必须不多于第B个小朋友分到的糖果;
Output
输出一行,表示lxhgww老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出-1。
Sample Input
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
Sample Output
11
Hint
对于30%的数据,保证 N<=100
对于100%的数据,保证 N<=100000
对于所有的数据,保证 K<=100000,1<=X<=5,1<=A, B<=N
注意使用long long
重要结论1:
以Xi-Xj<=C为约束条件,建图求最短路后得到的是最大解。
所有解都不大于且尽可能逼进Dis[X0]。
若有Xi-Xj<=C,可在图中连一条从Xj出发指向Xi的有向边,边的权值为C。再添加一个源点X0,从X0出发往X1,X2,...,Xn连一条权值为0的有向边。
然后以X0为起点,求单源最短路(无负权环),求出的起点X0到每个点的距离Dis[ ]就是不等式组的一组解。
若一开始就把Dis[X0]的值定死为A,再求最短路,那么求出的其它点的Dis[ ]值一定不会大于A。并且,求出的Dis[ ]是不大于A且与A最接近的一组。也就是所有点的Dis[ ]都达到了最大,也称为最大解。
若有Xi-Xj>=C,可在图中连一条从Xj出发指向Xi的有向边,边的权值为C。再添加一个源点X0,从X0出发往X1,X2,...,Xn连一条权值为0的有向边。
然后以X0为起点,求单源最长路(无正权环),求出的起点X0到每个点的距离Dis[ ]就是不等式组的一组解。
若一开始就把Dis[X0]的值定死为A,再求最长路,那么求出的其它点的Dis[ ]值一定不会小于A。并且,求出的Dis[ ]是不小于A且与A最接近的一组。也就是所有点的Dis[ ]都达到了最小,也称为最小解。
重要结论2:
以Xi-Xj>=C为约束条件,建图求最长路后得到的是最小解。
所有解都不小于且尽可能逼进Dis[X0]。
差分约束小结
最短路:(对应最大解)
1.按Xi-Xj<=C的条件建图(从Xj出发向Xi连接权值为C的有向边)。
2.添加超级源点X0,从X0出发往每个点连接一条权值为0的边
3.固定Dis[X0]的值为A
4.以X0为源点求最短路(无负权回路的前提下),求出的每个点的Dis[ ]对应一组解。
5.所有的Dis[ ]一定都是<=A的最大解。
最长路:(对应最小解)
1.按Xi-Xj>=C的条件建图(从Xj出发向Xi连接权值为C的有向边)。
2.添加超级源点X0,从X0出发往每个点连接一条权值为0的边
3.固定Dis[X0]的值为A
4.以X0为源点求最长路(无正权回路的前提下),求出的每个点的Dis[ ]对应一组解。
5.所有的Dis[ ]一定都是>=A的最小解。
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<queue> const int inf=-1e9; using namespace std; long long dis[1000005],next[4000005],last[1000005]; long long cnt[1000005]; bool vis[1000005]; long long m=0; long long n,k; queue<long long>q; struct edge{ long long from,to,len; edge(){} edge(long long a,long long b,long long c){from=a;to=b;len=c;} }; edge line[4000005]; inline void read(long long &x){ char t; bool mark=false; for(;t=getchar(),t<'0'||t>'9';) if(t=='-') mark=1; for(x=t-'0',t=getchar();'0'<=t&&t<='9';x=x*10+t-'0',t=getchar()); x=mark?-x:x; } void add_edge(long long from,long long to,long long len){ m++; next[m]=last[from]; last[from]=m; line[m]=edge(from,to,len); } bool spfa(int s){ long long i,j,k,t,ans=0; for(i=1;i<=n;i++)dis[i]=inf; vis[s]=true; dis[s]=1; cnt[s]=1; q.push(s); while(q.size()){ t=q.front();q.pop();vis[t]=false; for(i=last[t];i;i=next[i]){ if(dis[line[i].to]<dis[line[i].from]+line[i].len){ dis[line[i].to]=dis[line[i].from]+line[i].len; if(vis[line[i].to]==false){ cnt[line[i].to]++; if(cnt[line[i].to]>=n){ return false; } q.push(line[i].to); vis[line[i].to]=true; } } } } return true; } int main(){ long long i,j,t,x,y,ans=0; cin>>n>>k; for(i=1;i<=k;i++){ read(t);read(x);read(y); if(t==1){ add_edge(x,y,0); add_edge(y,x,0); } else if(t==2){ if(x==y){ cout<<"-1";return 0; } add_edge(x,y,1); } else if(t==3){ add_edge(y,x,0); } else if(t==4){ if(x==y){ cout<<"-1";return 0; } add_edge(y,x,1); } else{ add_edge(x,y,0); } } for(i=n;i>=1;i--)add_edge(n+1,i,0);//数据有鬼,不能从1--n bool flag=spfa(n+1); if(flag){ for(i=1;i<=n;i++)ans=ans+dis[i]; cout<<ans; } else cout<<"-1"; return 0; }