【BZOJ2330】糖果(差分约束系统,强连通分量,拓扑排序)
题意:
幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww需要满足小朋友们的K个要求。幼儿园的糖果总是有限的,lxhgww想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
输入的第一行是两个整数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个小朋友分到的糖果;
对于所有的数据,保证 N<=100000,K<=100000,1<=X<=5,1<=A, B<=N
思路:第一反应应该是差分约束系统,但N的范围令人不放心,实际上裸SPFA也需要一些优化才能跑过去
知乎上有几位大佬说这题是tarjan缩点+拓扑排序,确实这种做法理论复杂度才是有保证的
先建立原图,对于等于关系连双向边,小于等于(和大于等于,显然等价)连单向边,先缩一次点,同一个分量里的人糖果数一定相等
再进行拓扑排序计算每一个分量的糖果数,环会导致无解,注意糖果数和前面推导不同时需要取MAX
最后特判下同一个分量里的边有没有不等的,有则无解
SPFA
1 var q:array[0..2000000]of longint; 2 head,vet,next,len,dis,time:array[1..200000]of longint; 3 inq:array[1..200000]of boolean; 4 n,m,i,x,a,b,tot:longint; 5 ans,tmp:int64; 6 7 procedure add(a,b,c:longint); 8 begin 9 inc(tot); 10 next[tot]:=head[a]; 11 vet[tot]:=b; 12 len[tot]:=c; 13 head[a]:=tot; 14 end; 15 16 procedure spfa; 17 var t,w,i,u,e,v:longint; 18 begin 19 fillchar(time,sizeof(time),0); 20 t:=0; w:=-1; 21 for i:=1 to n do 22 begin 23 dis[i]:=1; inc(w); q[w]:=i; inq[i]:=true; 24 end; 25 26 while t<=w do 27 begin 28 u:=q[t mod n]; inc(t); inq[u]:=false; 29 e:=head[u]; 30 while e<>0 do 31 begin 32 v:=vet[e]; 33 if dis[u]+len[e]>dis[v] then 34 begin 35 dis[v]:=dis[u]+len[e]; 36 if not inq[v] then 37 begin 38 inc(time[v]); 39 if time[v]>n then 40 begin 41 writeln(-1); ans:=-1; 42 exit; 43 end; 44 inc(w); q[w mod n]:=v; inq[v]:=true; 45 end; 46 end; 47 e:=next[e]; 48 end; 49 end; 50 end; 51 52 begin 53 54 read(n,m); 55 for i:=1 to m do 56 begin 57 read(x,a,b); 58 if (x and 1=0)and(a=b) then 59 begin 60 writeln(-1); 61 exit; 62 end; 63 case x of 64 1:begin add(b,a,0); add(a,b,0); end; 65 2:add(a,b,1); 66 3:add(b,a,0); 67 4:add(b,a,1); 68 5:add(a,b,0); 69 end; 70 end; 71 spfa; 72 if ans=0 then 73 begin 74 for i:=1 to n do ans:=ans+dis[i]; 75 76 writeln(ans); 77 end; 78 79 end.
tarjan+拓扑排序
1 #include<map> 2 #include<set> 3 #include<cmath> 4 #include<cstdio> 5 #include<vector> 6 #include<cstring> 7 #include<cstdlib> 8 #include<iostream> 9 #include<algorithm> 10 #include<queue> 11 using namespace std; 12 const int M=210000; 13 int head[M],vet[M],next[M],len[M],a[M],b[M],c[M],dfn[M],low[M],flag[M],ind[M],stack[M],s[M]; 14 int n,m,i,tot,id,top,cnt; 15 long long d[M],size[M]; 16 17 void add(int a,int b,int c) 18 { 19 next[++tot]=head[a]; 20 vet[tot]=b; 21 len[tot]=c; 22 head[a]=tot; 23 ind[b]++; 24 } 25 26 void swap(int &a,int &b) 27 { 28 int t; 29 t=a;a=b;b=t; 30 } 31 32 void dfs(int u) 33 { 34 int e,v; 35 flag[u]=1; 36 stack[++top]=u; 37 dfn[u]=low[u]=++cnt; 38 for(e=head[u];e;e=next[e]) 39 { 40 v=vet[e]; 41 if(!flag[v]) 42 { 43 dfs(v); 44 low[u]=min(low[u],low[v]); 45 } 46 else if(!s[v]) low[u]=min(low[u],low[v]); 47 } 48 if(low[u]==dfn[u]) 49 { 50 id++; 51 while(stack[top]!=u) 52 { 53 s[stack[top]]=id; 54 size[id]++; 55 top--; 56 } 57 s[stack[top]]=id; 58 size[id]++; 59 top--; 60 } 61 62 } 63 64 long long solve() 65 { 66 queue<int> q; 67 int num; 68 long long sum; 69 for(int i=1;i<=id;i++) 70 if(!ind[i]) 71 { 72 q.push(i); 73 d[i]=1; 74 } 75 if(q.empty()) return -1; 76 num=0; 77 while(!q.empty()) 78 { 79 int u=q.front(); q.pop(); num++; 80 for(int e=head[u];e;e=next[e]) 81 { 82 int v=vet[e]; 83 ind[v]--; 84 d[v]=max(d[v],d[u]+len[e]); 85 if(!ind[v]) q.push(v); 86 } 87 } 88 if(num<id) return -1; 89 else 90 { 91 sum=0; 92 for(int i=1;i<=id;i++) sum=sum+d[i]*size[i]; 93 return sum; 94 } 95 96 } 97 98 int main() 99 { 100 // freopen("bzoj2330.in","r",stdin); 101 // freopen("bzoj2330.out","w",stdout); 102 scanf("%d%d",&n,&m); 103 id=0; 104 for(i=1;i<=m;i++) 105 { 106 scanf("%d%d%d",&a[i],&b[i],&c[i]); 107 if(a[i]==3||a[i]==4) swap(b[i],c[i]); 108 if(a[i]==1) 109 { 110 add(b[i],c[i],0); 111 add(c[i],b[i],0); 112 } 113 if(a[i]==3||a[i]==5) add(b[i],c[i],0); 114 } 115 for(i=1;i<=n;i++) 116 if(!flag[i]) dfs(i); 117 //printf("%d\n",id); 118 memset(head,0,sizeof(head)); 119 memset(ind,0,sizeof(ind)); 120 tot=0; 121 for(i=1;i<=m;i++) 122 if(s[b[i]]!=s[c[i]]) 123 { 124 if(a[i]==3||a[i]==5) add(s[b[i]],s[c[i]],0); 125 else add(s[b[i]],s[c[i]],1); 126 } 127 else 128 { 129 if(a[i]==2||a[i]==4) 130 { 131 printf("-1\n"); 132 return 0; 133 } 134 } 135 printf("%lld\n",solve()); 136 return 0; 137 }