【scoi2011】糖果 P2242
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。
Hint
对于30%的数据,保证 N<=100。对于100%的数据,保证 N<=100000。对于所有的数据,保证 K<=100000,1<=X<=5,1<=A,B<=N
Solution
跟普通的差分约束类似:
当T=1,在(a,b)间连一条权值为0的无向边。
当T=2,从a向b引一条权值为1的有向边。
当T=3,从b向a引一条权值为0的有向边。
当T=4,从b向a引一条权值为1的有向边。
当T=5,从a向b引一条权值为0的有向边。
那么在这个图上跑最长路就行了。但是很明显会有不合法的情况,即图中有正环。零环是被允许的。
所以考虑如何分开正环和零环。我的做法是先在只有零边的图上缩掉所有的零环,重建图的时候把所有的正边和联通两个SCC的零边加回去。这样原来0/1混合环中隐藏的1边就会变成自环,并且只剩下了有用的0边。然后跑最长路,如果有环则输出-1。时间复杂度O(N+M)。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#define maxn 200005
#define int long long
using namespace std;
struct Edge{
int u;
int v;
int w;
int next;
}edge[maxn],edge2[maxn];
stack<int>stk;
int first[maxn],last[maxn],dfn[maxn],low[maxn],belong[maxn],first2[maxn],last2[maxn],rd[maxn],dp[maxn];
int node,x,y,z,dfn_TimeClock,cnt,node2,ans,n,m;
void addedge(int u,int v,int w){
edge[++node]=(Edge){u,v,w,0};
if(first[u]==0)first[u]=node;
else edge[last[u]].next=node;
last[u]=node;
}
void addedge2(int u,int v,int w){
edge2[++node2]=(Edge){u,v,w,0};
if(first2[u]==0)first2[u]=node2;
else edge2[last2[u]].next=node2;
last2[u]=node2;
}
void init(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld",&x,&y,&z);
if(x==1){
addedge(y,z,0);
addedge(z,y,0);
}
else if(x==2){
addedge(y,z,1);
}
else if(x==3){
addedge(z,y,0);
}
else if(x==4){
addedge(z,y,1);
}
else if(x==5){
addedge(y,z,0);
}
}
}
void tarjan(int s){
dfn[s]=low[s]=++dfn_TimeClock;
stk.push(s);
for(int q=first[s];q;q=edge[q].next){
int j=edge[q].v;
if(dfn[j]==0){
tarjan(j);
low[s]=min(low[s],low[j]);
}
else if(!belong[j]){
low[s]=min(low[s],dfn[j]);
}
}
if(low[s]==dfn[s]){
cnt++;
belong[s]=cnt;
while(stk.top()!=s){
belong[stk.top()]=cnt;
stk.pop();
}
stk.pop();
}
}
void toposort(){
queue<int>p;
for(int i=1;i<=cnt;i++){
if(rd[i]==0){
p.push(i);
dp[i]=1;
}
}
while(!p.empty()){
int t=p.front();
p.pop();
rd[t]=0;
for(int q=first2[t];q;q=edge2[q].next){
int j=edge2[q].v;
dp[j]=max(dp[j],dp[t]+edge2[q].w);
if(rd[j]>1)rd[j]--;
else p.push(j);
}
}
}
signed main(){
init();
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i);
}
for(int i=1;i<=n;i++){
for(int p=first[i];p;p=edge[p].next){
int j=edge[p].v;
if(belong[i]!=belong[j]||edge[p].w==1){
rd[belong[j]]++;
addedge2(belong[i],belong[j],edge[p].w);
}
}
}
toposort();
for(int i=1;i<=cnt;i++){
if(rd[i]!=0){
printf("-1");
return 0;
}
}
for(int i=1;i<=n;i++){
ans+=dp[belong[i]];
}
printf("%lld\n",ans);
return 0;
}