题面:
题解:
其实这两道是同一道题。。。。
最小割是用的dinic,不同的最小割是用的isap
其实都是分治求最小割
简单讲讲思路吧
就是首先全部的点都在一个集合里,然后随意定两个点为s和t,这里默认是第一个和最后一个。
然后找到最小割,最小割将整张图分为了s集和t集,于是我们再用这个最小割更新跨集合点对之间的最小割。
这个很好理解,因为当前找到的最小割将s集和t集分开了,显然对于任意一组跨集合的点对而言,当前最小割都是一个可能的最小割。
然后我们再递归处理s集和t集(重复以上步骤)。
每次找到最小割后就更新跨集合点对。
本质上是分治吧。
之前看有些地方提到了最小割树,这里放个链接(这是我找到的写的最全的一篇了)
下面放代码吧,个人觉得看代码会更好理解,尤其是对分治不熟悉的人(比如我)
不同的最小割(isap):
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 900 5 #define ac 20000 6 #define inf 2139062143 7 #define D printf("line in %d ", __LINE__); 8 char READ[7000100], *o = READ; 9 int n, m, s, addflow, t, answer, tt; 10 int last[AC], c[AC], have[AC], a[AC], ans[AC][AC], good[AC]; 11 int Head[AC], date[ac], Next[ac], haveflow[ac], tot = 1; 12 int q[AC], head, tail; 13 int ss[400000], cnt; 14 bool z[AC]; 15 16 inline int read() 17 { 18 int x = 0; char c = getchar(); 19 while(c > '9' || c < '0') c = getchar(); 20 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 21 return x; 22 } 23 24 inline void add(int f, int w, int S) 25 { 26 date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, haveflow[tot] = S; 27 date[++tot] = f, Next[tot] = Head[w], Head[w] = tot, haveflow[tot] = S; 28 //printf("%d ---> %d %d ", f, w, S); 29 } 30 31 inline void upmin(int &a, int b) 32 { 33 if(b < a) a = b; 34 } 35 36 void pre() 37 { 38 int u, v, e; 39 n = read(), m = read(); 40 for(R i = 1; i <= n; i++) a[i] = i; 41 for(R i = 1; i <= m; i++) 42 { 43 u = read(), v = read(), e = read(); 44 add(u, v, e); 45 } 46 memset(ans, 127, sizeof(ans)); 47 } 48 49 bool bfs() 50 { 51 int x, now; 52 memset(have, 0, sizeof(have)); 53 memset(c, 0, sizeof(c)); 54 have[1] = 1, c[t] = 1, x = t; 55 head = tail = 0; 56 q[++tail] = t; 57 while(head < tail) 58 { 59 x = q[++head]; 60 for(R i = Head[x]; i ; i = Next[i]) 61 { 62 now = date[i]; 63 if(haveflow[i] && !c[now]) 64 { 65 c[now] = c[x] + 1; 66 ++have[c[now]];//error...忘记统计了 67 q[++tail] = now; 68 } 69 } 70 } 71 memcpy(good, Head, sizeof(Head)); 72 return c[s]; 73 } 74 75 inline void aru() 76 { 77 int x = t; 78 while(x != s) 79 { 80 haveflow[last[x]] -= addflow; 81 haveflow[last[x] ^ 1] += addflow; 82 x = date[last[x] ^ 1]; 83 } 84 tt += addflow; 85 } 86 87 void isap() 88 { 89 int x = s, now; bool done; 90 tt = 0, addflow = inf; 91 while(c[s] != 875) 92 { 93 if(x == t) aru(), addflow = inf, x = s;//忘记设置全局了,,,那在这里手动改一下吧 94 done = false; 95 for(R i = good[x]; i ; i = Next[i]) 96 { 97 now = date[i]; 98 if(haveflow[i] && c[now] == c[x] - 1) 99 { 100 upmin(addflow, haveflow[i]); 101 done = true; 102 last[now] = i; 103 good[x] = i; 104 x = now; 105 break; 106 } 107 } 108 if(!done) 109 { 110 int go = 874; 111 for(R i=Head[x]; i ; i = Next[i]) 112 { 113 now = date[i]; 114 if(haveflow[i] && c[now]) upmin(go, c[now]); 115 } 116 good[x] = Head[x]; 117 if(!(--have[c[x]])) break; 118 ++have[c[x] = go + 1]; 119 if(x != s) x = date[last[x] ^ 1]; 120 } 121 } 122 } 123 124 void restore()//还原 125 { 126 for(R i = 2; i <= tot; i += 2)//对于无向图而言,这还是非常妙的 127 haveflow[i] = haveflow[i ^ 1] = (haveflow[i] + haveflow[i ^ 1]) / 2; 128 } 129 130 void dfs(int x) 131 { 132 int now; 133 z[x] = true;//.... 134 for(R i = Head[x]; i ; i = Next[i]) 135 { 136 now = date[i]; 137 if(haveflow[i] && !z[now]) dfs(now); 138 } 139 } 140 141 void solve(int l, int r) 142 { 143 if(l == r) return ; 144 s = a[l], t = a[r]; 145 restore(); 146 //printf("%d %d ", l, r); 147 bfs();//重新定层次 148 isap(); 149 memset(z, 0, sizeof(z)); 150 dfs(s); 151 for(R i=1;i<=n;i++)//更新最小割 152 if(z[i]) 153 for(R j=1;j<=n;j++) 154 if(!z[j]) 155 ans[i][j] = ans[j][i] = min(ans[i][j], tt); 156 int ll = l - 1, rr = r + 1; 157 for(R i = l; i <= r; i++) 158 if(z[a[i]]) q[++ll] = a[i]; 159 else q[--rr] = a[i];//不知道取什么名字了,先借这个用一下吧 160 for(R i = l; i <= r; i++) a[i] = q[i]; 161 solve(l, ll), solve(rr, r); 162 } 163 164 void work() 165 { 166 for(R i=1;i<=n;i++) 167 for(R j=1;j<i;j++) 168 ss[++cnt] = ans[i][j]; 169 sort(ss + 1, ss + cnt + 1); 170 for(R i=1;i<=cnt;i++) 171 if(ss[i] != ss[i + 1]) ++answer; 172 printf("%d ", answer); 173 } 174 175 int main() 176 { 177 // freopen("in.in","r",stdin); 178 pre(); 179 solve(1, n); 180 work(); 181 // fclose(stdin); 182 return 0; 183 }
最小割(dinic):
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define LL long long 5 #define inf 2139062143 6 #define getchar() *o++ 7 #define AC 155 8 #define ac 101000 9 #define D printf("line in %d ", __LINE__); 10 11 char READ[7001000], *o = READ; 12 int s, t, T, Q, n, m; 13 int last[AC], ans[AC][AC], a[AC], tmp[AC], c[AC]; 14 int date[ac], Next[ac], haveflow[ac], Head[AC], tot; 15 int q[AC], head, tail; 16 bool z[AC]; 17 18 inline int read() 19 { 20 int x = 0;char c = getchar();bool z = false; 21 while(c > '9' || c < '0') c = getchar(); 22 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 23 if(!z) return x; 24 else return -x; 25 } 26 27 inline void add(int f, int w, int S) 28 {//因为是双向边,所以反向边就可以省掉了 29 date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, haveflow[tot] = S; 30 date[++tot] = f, Next[tot] = Head[w], Head[w] = tot, haveflow[tot] = S; 31 // printf("%d ---> %d %d ", f, w, S); 32 } 33 34 bool bfs() 35 { 36 int now, x; 37 head = tail = 0; 38 memset(c, 0, sizeof(c)); 39 c[s] = 1, q[++tail] = s; 40 while(head < tail) 41 { 42 x = q[++head]; 43 for(R i = Head[x]; i ; i = Next[i]) 44 { 45 now = date[i]; 46 if(haveflow[i] && !c[now]) 47 { 48 c[now] = c[x] + 1; 49 q[++tail] = now; 50 } 51 } 52 } 53 return c[t]; 54 } 55 56 int dfs(int x, int flow) 57 { 58 if(x == t) return flow; 59 int addflow, used = 0, now; 60 for(R i=Head[x]; i ; i = Next[i]) 61 { 62 now = date[i]; 63 if(c[now] == c[x] + 1) 64 {//流到下一个点去的流量在haveflow和剩余流量中取最小值 65 addflow = dfs(now, min(haveflow[i], flow - used)); 66 haveflow[i] -= addflow; 67 haveflow[i^1] += addflow; 68 used += addflow; 69 if(used == flow) return flow; 70 } 71 } 72 if(!used) c[x] = 0; 73 return used; 74 } 75 76 int dinic() 77 { 78 int rnt = 0; 79 while(bfs()) rnt += dfs(s, inf); 80 return rnt; 81 } 82 83 void DFS(int x)//标记能到的点,便于区分 84 { 85 int now; 86 z[x]= 1; 87 for(R i = Head[x]; i ; i = Next[i]) 88 { 89 now = date[i]; 90 if(haveflow[i] && !z[now]) DFS(now); 91 } 92 } 93 94 void restore()//挺妙的还原流量的方法,因为这两条边即是双向边,又可以当反向边 95 {//因此总流量肯定是不会变的,所以加起来取个平均值就相当于还原了 96 for(R i = 2; i <= tot; i += 2) 97 haveflow[i] = haveflow[i+1] = (haveflow[i] + haveflow[i+1]) / 2; 98 } 99 100 void solve(int l, int r) 101 { 102 if(l == r) return ; 103 restore(); 104 s = a[l], t = a[r];//随便选两个作为s和t 105 int tt = dinic();//跑最小割 106 memset(z, 0, sizeof(z)); 107 DFS(s);//查看哪些点是能到的(即没有被割断) 108 for(R i=1;i<=n;i++)//这里是在每次找到一个最小割的时候,就对跨集合点对的割进行更新, 109 if(z[i])//因此每次是需要枚举所有点对的。不然的话因为i是枚举的s集里面的东西,可能会导致某些点对没有被统计到 110 for(R j=1;j<=n;j++)//获取跨集合点对的割的大小 111 if(!z[j]) ans[i][j] = ans[j][i] = min(ans[i][j], tt); 112 int ll = l - 1, rr = r + 1; 113 for(R i = l; i <= r; i++) 114 if(z[a[i]])//如果这个点可以属于s集 115 tmp[++ll] = a[i];//加入左边(从左边开始塞) 116 else tmp[--rr] = a[i];//不然就放在右边 117 for(R i=l;i<=r;i++) a[i] = tmp[i];//类似于归并排序的,,,放回数组内 118 solve(l, ll), solve(rr, r); 119 } 120 121 void pre() 122 { 123 int u, v, k; 124 tot = 1; 125 memset(ans, 127, sizeof(ans)); 126 memset(Head, 0, sizeof(Head)); 127 n = read(), m = read(); 128 for(R i=1;i<=n;i++) a[i] = i;//先把点按顺序放进来 129 for(R i=1;i<=m;i++) 130 { 131 u = read(), v = read(), k = read(); 132 add(u, v, k); 133 } 134 solve(1, n);//找出所有最小割 135 } 136 137 void work() 138 { 139 int k, cnt; 140 Q = read(); 141 while(Q--) 142 { 143 k = read(), cnt = 0; 144 for(R i=1;i<=n;i++) 145 for(R j=1;j<i;j++)//不能一个点对重复统计了 146 if(ans[i][j] <= k) ++cnt; 147 printf("%d ", cnt); 148 } 149 printf(" "); 150 } 151 152 int main() 153 { 154 // freopen("in.in","r",stdin); 155 fread(READ, 1, 7000000, stdin); 156 T = read(); 157 while(T--) 158 { 159 pre(); 160 work(); 161 } 162 // fclose(stdin); 163 return 0; 164 }