算法详解

等价流树正如其名,树上两点间的路径上的边权最小值为图上两点间的最小割。

Gusfield算法就是建等价流树的一种算法。设当前正在处理的集合为 \(S(|S|\ge 2)\),从 \(S\) 中任选两个点 \(x,y\),求出 \(x,y\) 间的最小割也就是最大流 \(flow\),此时在最小割树中加入一条从 \(x\) 到 \(y\),边权为 \(flow\)。在原图中去掉所有的满流边后,\(S\) 会变成两部分,一部分是在残量网络上从起点能遍历到的点,另一部分是遍历不到的点,分治下去求解即可,很容易可以发现,最多求解 \(n-1\) 次,所以得到的必然是棵树。

证明这个做法建出来的树必然满足树上路径边权最小值等于原图上两点间的最小割。

有一种不严谨的证明,因为递归出来的两边都是合法的等价流树,假设这两棵等价流树中的边权全部大于 \((s,t)\) 这条边,那么显然割掉 \((s,t)\) 代表的原图中的边集会优于割掉其它边代表的原图中的边集。即若存在 \(u,v\) 分别再 \(s,t\) 侧,\((s,t)\) 这条边的边权必然为 \(u,v\) 的最小割。至于更详细的证明可以研读2016年王文涛国集论文

例题

1.【模板】最小割树(Gomory-Hu Tree)

其实我并不觉得这是GHT的模板,它只用到了大小,并没有用到方案等其它信息。直接建出等价流树,每次询问在等价流树上找路径边权最小值即可,倍增预处理皆可。

点击查看代码
  1. #include<bits/stdc++.h>
  2. #define ull unsigned long long
  3. #define ll long long
  4. #define pdi pair<double,int>
  5. #define pii pair<int,int>
  6. #define pb push_back
  7. #define mp make_pair
  8. #define eps 1e-9
  9. using namespace std;
  10. namespace IO{
  11. template<typename T>
  12. inline void read(T &x){
  13. x=0;
  14. int f=1;
  15. char ch=getchar();
  16. while(ch>'9'||ch<'0'){
  17. if(ch=='-'){
  18. f=-1;
  19. }
  20. ch=getchar();
  21. }
  22. while(ch>='0'&&ch<='9'){
  23. x=x*10+(ch-'0');
  24. ch=getchar();
  25. }
  26. x=(f==1?x:-x);
  27. }
  28. template<typename T>
  29. inline void write(T x){
  30. if(x<0){
  31. putchar('-');
  32. x=-x;
  33. }
  34. if(x>=10){
  35. write(x/10);
  36. }
  37. putchar(x%10+'0');
  38. }
  39. template<typename T>
  40. inline void write_endl(T x){
  41. write(x);
  42. putchar('\n');
  43. }
  44. template<typename T>
  45. inline void write_space(T x){
  46. write(x);
  47. putchar(' ');
  48. }
  49. }
  50. using namespace IO;
  51. const int N=510,M=6010,Lg=11,inf=1e9;
  52. int n,m,head[N],hd[N],tot1=0,tot=1,id[N];
  53. struct edge{
  54. int v,w,nxt;
  55. }e[M],G[M];
  56. void add(int u,int v,int w){
  57. e[++tot].v=v;
  58. e[tot].nxt=head[u];
  59. e[tot].w=w;
  60. head[u]=tot;
  61. }
  62. void add1(int u,int v,int w){
  63. G[++tot1].v=v;
  64. G[tot1].w=w;
  65. G[tot1].nxt=hd[u];
  66. hd[u]=tot1;
  67. }
  68. namespace GHT{
  69. int dep[N],cur[N],tmp1[N],tmp2[N];
  70. bool bfs(int S,int T){
  71. for(int i=1;i<=n;i++){
  72. dep[i]=0;
  73. }
  74. queue<int>q;
  75. q.push(S);
  76. dep[S]=1;
  77. while(!q.empty()){
  78. int u=q.front();
  79. q.pop();
  80. for(int i=head[u];i;i=e[i].nxt){
  81. int v=e[i].v,w=e[i].w;
  82. if(w&&!dep[v]){
  83. dep[v]=dep[u]+1;
  84. q.push(v);
  85. if(v==T){
  86. return 1;
  87. }
  88. }
  89. }
  90. }
  91. return 0;
  92. }
  93. int dfs(int u,int flow,int T){
  94. if(u==T){
  95. return flow;
  96. }
  97. int s=0;
  98. for(int i=cur[u];i;i=e[i].nxt){
  99. cur[u]=i;
  100. int v=e[i].v,w=e[i].w;
  101. if(w&&dep[v]==dep[u]+1){
  102. int res=dfs(v,min(flow,w),T);
  103. e[i].w-=res;
  104. e[i^1].w+=res;
  105. flow-=res;
  106. s+=res;
  107. }
  108. if(!flow){
  109. break;
  110. }
  111. }
  112. if(!s){
  113. dep[u]=0;
  114. }
  115. return s;
  116. }
  117. void init(){
  118. for(int i=0;i<=tot;i+=2){
  119. e[i].w+=e[i^1].w;
  120. e[i^1].w=0;
  121. }
  122. }
  123. int dinic(int S,int T){
  124. init();
  125. int ans=0;
  126. while(bfs(S,T)){
  127. memcpy(cur,head,sizeof(head));
  128. ans+=dfs(S,inf,T);
  129. }
  130. return ans;
  131. }
  132. void build(int l,int r){
  133. if(l>=r){
  134. return;
  135. }
  136. int x=id[l],y=id[l+1];
  137. int flow=dinic(x,y);
  138. add1(x,y,flow);
  139. add1(y,x,flow);
  140. int cnt1=0,cnt2=0;
  141. for(int i=l;i<=r;i++){
  142. if(dep[id[i]]){
  143. tmp1[++cnt1]=id[i];
  144. }
  145. else{
  146. tmp2[++cnt2]=id[i];
  147. }
  148. }
  149. for(int i=1;i<=cnt1;i++){
  150. id[l+i-1]=tmp1[i];
  151. }
  152. for(int i=1;i<=cnt2;i++){
  153. id[cnt1+i+l-1]=tmp2[i];
  154. }
  155. build(l,l+cnt1-1);
  156. build(l+cnt1,r);
  157. }
  158. }
  159. int f[N][Lg+5],mn[N][Lg+5],dep[N];
  160. void make_tree(int u,int father){
  161. f[u][0]=father;
  162. dep[u]=dep[father]+1;
  163. for(int i=1;i<=Lg;i++){
  164. f[u][i]=f[f[u][i-1]][i-1];
  165. mn[u][i]=min(mn[u][i-1],mn[f[u][i-1]][i-1]);
  166. }
  167. for(int i=hd[u];i;i=G[i].nxt){
  168. int v=G[i].v,w=G[i].w;
  169. if(v==father){
  170. continue;
  171. }
  172. mn[v][0]=w;
  173. make_tree(v,u);
  174. }
  175. }
  176. int lca(int u,int v){
  177. if(dep[u]<dep[v]){
  178. swap(u,v);
  179. }
  180. int mini=inf;
  181. for(int i=Lg;i>=0;i--){
  182. if(dep[f[u][i]]>=dep[v]){
  183. mini=min(mini,mn[u][i]);
  184. u=f[u][i];
  185. }
  186. }
  187. if(u==v){
  188. return mini;
  189. }
  190. for(int i=Lg;i>=0;i--){
  191. if(f[u][i]!=f[v][i]){
  192. mini=min(mini,min(mn[u][i],mn[v][i]));
  193. u=f[u][i],v=f[v][i];
  194. }
  195. }
  196. return min(mini,min(mn[u][0],mn[v][0]));
  197. }
  198. signed main(){
  199. #ifndef ONLINE_JUDGE
  200. freopen("1.in","r",stdin);
  201. freopen("1.out","w",stdout);
  202. #endif
  203. memset(mn,0x3f,sizeof(mn));
  204. read(n),read(m);
  205. for(int i=1,u,v,w;i<=m;i++){
  206. read(u),read(v),read(w);
  207. add(u,v,w);
  208. add(v,u,0);
  209. add(v,u,w);
  210. add(u,v,0);
  211. }
  212. for(int i=1;i<=n;i++){
  213. id[i]=i;
  214. }
  215. GHT::build(1,n);
  216. make_tree(1,0);
  217. int q;
  218. read(q);
  219. while(q--){
  220. int u,v;
  221. read(u),read(v);
  222. write_endl(lca(u,v));
  223. }
  224. return 0;
  225. }

2.[ZJOI2011]最小割

也是个板题,建出等价流树,如果不连通,就每个连通块之间建一条长度为 \(0\) 的边,最后询问时在树上以每个点为起点扫一遍即可。

点击查看代码
  1. #include<bits/stdc++.h>
  2. #define ull unsigned long long
  3. #define int long long
  4. #define pdi pair<double,int>
  5. #define pii pair<int,int>
  6. #define pb push_back
  7. #define mp make_pair
  8. #define eps 1e-9
  9. using namespace std;
  10. namespace IO{
  11. template<typename T>
  12. inline void read(T &x){
  13. x=0;
  14. int f=1;
  15. char ch=getchar();
  16. while(ch>'9'||ch<'0'){
  17. if(ch=='-'){
  18. f=-1;
  19. }
  20. ch=getchar();
  21. }
  22. while(ch>='0'&&ch<='9'){
  23. x=x*10+(ch-'0');
  24. ch=getchar();
  25. }
  26. x=(f==1?x:-x);
  27. }
  28. template<typename T>
  29. inline void write(T x){
  30. if(x<0){
  31. putchar('-');
  32. x=-x;
  33. }
  34. if(x>=10){
  35. write(x/10);
  36. }
  37. putchar(x%10+'0');
  38. }
  39. template<typename T>
  40. inline void write_endl(T x){
  41. write(x);
  42. putchar('\n');
  43. }
  44. template<typename T>
  45. inline void write_space(T x){
  46. write(x);
  47. putchar(' ');
  48. }
  49. }
  50. using namespace IO;
  51. const int N=510,M=60010,inf=1e12;
  52. int n,m,head[N],hd[N],tot1=0,tot=1,id[N],fa[N];
  53. struct edge{
  54. int v,w,nxt;
  55. }e[M],G[M];
  56. int getfa(int x){
  57. if(fa[x]!=x){
  58. fa[x]=getfa(fa[x]);
  59. }
  60. return fa[x];
  61. }
  62. void merge(int u,int v){
  63. u=getfa(u),v=getfa(v);
  64. if(u!=v){
  65. fa[v]=u;
  66. }
  67. }
  68. void add(int u,int v,int w){
  69. e[++tot].v=v;
  70. e[tot].nxt=head[u];
  71. e[tot].w=w;
  72. head[u]=tot;
  73. }
  74. void add1(int u,int v,int w){
  75. G[++tot1].v=v;
  76. G[tot1].w=w;
  77. G[tot1].nxt=hd[u];
  78. hd[u]=tot1;
  79. merge(u,v);
  80. }
  81. namespace GHT{
  82. int dep[N],cur[N],tmp1[N],tmp2[N];
  83. bool bfs(int S,int T){
  84. for(int i=1;i<=n;i++){
  85. dep[i]=0;
  86. }
  87. queue<int>q;
  88. q.push(S);
  89. dep[S]=1;
  90. while(!q.empty()){
  91. int u=q.front();
  92. q.pop();
  93. for(int i=head[u];i;i=e[i].nxt){
  94. int v=e[i].v,w=e[i].w;
  95. if(w&&!dep[v]){
  96. dep[v]=dep[u]+1;
  97. q.push(v);
  98. if(v==T){
  99. return 1;
  100. }
  101. }
  102. }
  103. }
  104. return 0;
  105. }
  106. int dfs(int u,int flow,int T){
  107. if(u==T){
  108. return flow;
  109. }
  110. int s=0;
  111. for(int i=cur[u];i;i=e[i].nxt){
  112. cur[u]=i;
  113. int v=e[i].v,w=e[i].w;
  114. if(w&&dep[v]==dep[u]+1){
  115. int res=dfs(v,min(flow,w),T);
  116. e[i].w-=res;
  117. e[i^1].w+=res;
  118. flow-=res;
  119. s+=res;
  120. }
  121. if(!flow){
  122. break;
  123. }
  124. }
  125. if(!s){
  126. dep[u]=0;
  127. }
  128. return s;
  129. }
  130. void init(){
  131. for(int i=0;i<=tot;i+=2){
  132. e[i].w+=e[i^1].w;
  133. e[i^1].w=0;
  134. }
  135. }
  136. int dinic(int S,int T){
  137. init();
  138. int ans=0;
  139. while(bfs(S,T)){
  140. memcpy(cur,head,sizeof(head));
  141. ans+=dfs(S,inf,T);
  142. }
  143. return ans;
  144. }
  145. void build(int l,int r){
  146. if(l>=r){
  147. return;
  148. }
  149. int x=id[l],y=id[l+1];
  150. int flow=dinic(x,y);
  151. add1(x,y,flow);
  152. add1(y,x,flow);
  153. int cnt1=0,cnt2=0;
  154. for(int i=l;i<=r;i++){
  155. if(dep[id[i]]){
  156. tmp1[++cnt1]=id[i];
  157. }
  158. else{
  159. tmp2[++cnt2]=id[i];
  160. }
  161. }
  162. for(int i=1;i<=cnt1;i++){
  163. id[l+i-1]=tmp1[i];
  164. }
  165. for(int i=1;i<=cnt2;i++){
  166. id[cnt1+i+l-1]=tmp2[i];
  167. }
  168. build(l,l+cnt1-1);
  169. build(l+cnt1,r);
  170. }
  171. }
  172. void clear(){
  173. for(int i=1;i<=n;i++){
  174. head[i]=hd[i]=0;
  175. id[i]=i;
  176. fa[i]=i;
  177. }
  178. tot=tot1=1;
  179. }
  180. int ans,x;
  181. void dfs(int u,int father,int mn){
  182. if(mn<=x){
  183. ans++;
  184. }
  185. for(int i=hd[u];i;i=G[i].nxt){
  186. int v=G[i].v,w=G[i].w;
  187. if(v==father){
  188. continue;
  189. }
  190. dfs(v,u,min(mn,w));
  191. }
  192. }
  193. void solve(){
  194. read(n),read(m);
  195. clear();
  196. for(int i=1,u,v,w;i<=m;i++){
  197. read(u),read(v),read(w);
  198. add(u,v,w);
  199. add(v,u,0);
  200. add(v,u,w);
  201. add(u,v,0);
  202. }
  203. GHT::build(1,n);
  204. for(int i=2;i<=n;i++){
  205. if(getfa(i)!=getfa(1)){
  206. add1(i,1,0);
  207. merge(1,i);
  208. }
  209. }
  210. int q;
  211. read(q);
  212. while(q--){
  213. read(x);
  214. ans=0;
  215. for(int i=1;i<=n;i++){
  216. dfs(i,0,inf);
  217. }
  218. write_endl(ans/2);
  219. }
  220. }
  221. signed main(){
  222. #ifndef ONLINE_JUDGE
  223. freopen("1.in","r",stdin);
  224. freopen("1.out","w",stdout);
  225. #endif
  226. int t;
  227. read(t);
  228. while(t--){
  229. solve();
  230. puts("");
  231. }
  232. return 0;
  233. }

3.[CQOI2016]不同的最小割

又一个板题,建出等价流树,根据性质可得,两点间的最小割为两点在等价流树上的路径上的边权最小值,所以统计有等价流树多少种不同边权即可。

点击查看代码
  1. #include<bits/stdc++.h>
  2. #define ull unsigned long long
  3. #define ll long long
  4. #define pdi pair<double,int>
  5. #define pii pair<int,int>
  6. #define pb push_back
  7. #define mp make_pair
  8. #define eps 1e-9
  9. using namespace std;
  10. namespace IO{
  11. template<typename T>
  12. inline void read(T &x){
  13. x=0;
  14. int f=1;
  15. char ch=getchar();
  16. while(ch>'9'||ch<'0'){
  17. if(ch=='-'){
  18. f=-1;
  19. }
  20. ch=getchar();
  21. }
  22. while(ch>='0'&&ch<='9'){
  23. x=x*10+(ch-'0');
  24. ch=getchar();
  25. }
  26. x=(f==1?x:-x);
  27. }
  28. template<typename T>
  29. inline void write(T x){
  30. if(x<0){
  31. putchar('-');
  32. x=-x;
  33. }
  34. if(x>=10){
  35. write(x/10);
  36. }
  37. putchar(x%10+'0');
  38. }
  39. template<typename T>
  40. inline void write_endl(T x){
  41. write(x);
  42. putchar('\n');
  43. }
  44. template<typename T>
  45. inline void write_space(T x){
  46. write(x);
  47. putchar(' ');
  48. }
  49. }
  50. using namespace IO;
  51. const int N=1010,M=600010,Lg=11,inf=1e9;
  52. int n,m,head[N],hd[N],tot1=0,tot=1,id[N];
  53. struct edge{
  54. int v,w,nxt;
  55. }e[M];
  56. void add(int u,int v,int w){
  57. e[++tot].v=v;
  58. e[tot].nxt=head[u];
  59. e[tot].w=w;
  60. head[u]=tot;
  61. }
  62. set<int>s;
  63. namespace GHT{
  64. int dep[N],cur[N],tmp1[N],tmp2[N];
  65. bool bfs(int S,int T){
  66. for(int i=1;i<=n;i++){
  67. dep[i]=0;
  68. }
  69. queue<int>q;
  70. q.push(S);
  71. dep[S]=1;
  72. while(!q.empty()){
  73. int u=q.front();
  74. q.pop();
  75. for(int i=head[u];i;i=e[i].nxt){
  76. int v=e[i].v,w=e[i].w;
  77. if(w&&!dep[v]){
  78. dep[v]=dep[u]+1;
  79. q.push(v);
  80. if(v==T){
  81. return 1;
  82. }
  83. }
  84. }
  85. }
  86. return 0;
  87. }
  88. int dfs(int u,int flow,int T){
  89. if(u==T){
  90. return flow;
  91. }
  92. int s=0;
  93. for(int i=cur[u];i;i=e[i].nxt){
  94. cur[u]=i;
  95. int v=e[i].v,w=e[i].w;
  96. if(w&&dep[v]==dep[u]+1){
  97. int res=dfs(v,min(flow,w),T);
  98. e[i].w-=res;
  99. e[i^1].w+=res;
  100. flow-=res;
  101. s+=res;
  102. }
  103. if(!flow){
  104. break;
  105. }
  106. }
  107. if(!s){
  108. dep[u]=0;
  109. }
  110. return s;
  111. }
  112. void init(){
  113. for(int i=0;i<=tot;i+=2){
  114. e[i].w+=e[i^1].w;
  115. e[i^1].w=0;
  116. }
  117. }
  118. int dinic(int S,int T){
  119. init();
  120. int ans=0;
  121. while(bfs(S,T)){
  122. memcpy(cur,head,sizeof(head));
  123. ans+=dfs(S,inf,T);
  124. }
  125. return ans;
  126. }
  127. void build(int l,int r){
  128. if(l>=r){
  129. return;
  130. }
  131. int x=id[l],y=id[l+1];
  132. int flow=dinic(x,y);
  133. s.insert(flow);
  134. int cnt1=0,cnt2=0;
  135. for(int i=l;i<=r;i++){
  136. if(dep[id[i]]){
  137. tmp1[++cnt1]=id[i];
  138. }
  139. else{
  140. tmp2[++cnt2]=id[i];
  141. }
  142. }
  143. for(int i=1;i<=cnt1;i++){
  144. id[l+i-1]=tmp1[i];
  145. }
  146. for(int i=1;i<=cnt2;i++){
  147. id[cnt1+i+l-1]=tmp2[i];
  148. }
  149. build(l,l+cnt1-1);
  150. build(l+cnt1,r);
  151. }
  152. }
  153. signed main(){
  154. #ifndef ONLINE_JUDGE
  155. freopen("1.in","r",stdin);
  156. freopen("1.out","w",stdout);
  157. #endif
  158. read(n),read(m);
  159. for(int i=1,u,v,w;i<=m;i++){
  160. read(u),read(v),read(w);
  161. add(u,v,w);
  162. add(v,u,0);
  163. add(v,u,w);
  164. add(u,v,0);
  165. }
  166. for(int i=1;i<=n;i++){
  167. id[i]=i;
  168. }
  169. GHT::build(1,n);
  170. write_endl(s.size());
  171. return 0;
  172. }

4.cf343e

先想最暴力的做法,是不是枚举排列,然后跑最小割,求最小割的和的最大值。然后我们发现这样要把任意两个点之间的最小割都求出来,于是建出等价流树。

此时我们再看一下一个排列表示什么,表示的是给每个点标一个dfs序,然后按照dfs序在最小割树上搜一遍,因为等价流树的性质,所以最小割的和等于dfs序相邻的两点的路径上边权最小值的和。所以问题就转化为了求一个dfs序,使得最小割树上边权小的边尽量少走。

先讨论最小边的贡献的次数,只要经过最小边则必然造成一次贡献。因为要遍历整棵树,所以最小边至少经过一次,即最少造成一次贡献。

那么有可能造成更多的贡献吗?这是不可能的。

令最小边的两端点为 \(u,v\),若断掉最小边,则一棵树会变成两颗树,分别令 \(u,v\) 为根,那么这两棵树分别为树 \(u\) 和树 \(v\)。一个非常显然的事是,我们可以先遍历完树 \(u\),再遍历树 \(v\),这样最小边就只会造成 \(1\) 次贡献,同理每条边都会造成 \(1\) 次贡献,答案的最大值就为等价流树上边权之和,方案可以 \(n^2\) 扫一遍。

点击查看代码
  1. #include<bits/stdc++.h>
  2. #define ull unsigned long long
  3. #define ll long long
  4. #define pii pair<int,int>
  5. #define pb push_back
  6. #define mp make_pair
  7. using namespace std;
  8. namespace IO{
  9. template<typename T>
  10. inline void read(T &x){
  11. x=0;
  12. int f=1;
  13. char ch=getchar();
  14. while(ch>'9'||ch<'0'){
  15. if(ch=='-'){
  16. f=-1;
  17. }
  18. ch=getchar();
  19. }
  20. while(ch>='0'&&ch<='9'){
  21. x=x*10+(ch-'0');
  22. ch=getchar();
  23. }
  24. x=(f==1?x:-x);
  25. }
  26. template<typename T>
  27. inline void write(T x){
  28. if(x<0){
  29. putchar('-');
  30. x=-x;
  31. }
  32. if(x>=10){
  33. write(x/10);
  34. }
  35. putchar(x%10+'0');
  36. }
  37. template<typename T>
  38. inline void write_endl(T x){
  39. write(x);
  40. putchar('\n');
  41. }
  42. template<typename T>
  43. inline void write_space(T x){
  44. write(x);
  45. putchar(' ');
  46. }
  47. }
  48. using namespace IO;
  49. const int N=300,M=1e4,inf=1e9;
  50. int n,m,tot=1,tot1=1,head[N],hd[N],id[N],ans;
  51. struct edge{
  52. int u,v,w,nxt;
  53. }e[M],G[M];
  54. void add(int u,int v,int w){
  55. e[++tot].v=v;
  56. e[tot].w=w;
  57. e[tot].nxt=head[u];
  58. head[u]=tot;
  59. }
  60. void add1(int u,int v,int w){
  61. G[++tot1].v=v;
  62. G[tot1].u=u;
  63. G[tot1].w=w;
  64. G[tot1].nxt=hd[u];
  65. ans+=w;
  66. hd[u]=tot1;
  67. }
  68. namespace Gusfield{
  69. int dep[N],cur[N],tmp1[N],tmp2[N];
  70. bool bfs(int S,int T){
  71. for(int i=1;i<=n;i++){
  72. dep[i]=0;
  73. }
  74. queue<int>q;
  75. q.push(S);
  76. dep[S]=1;
  77. while(!q.empty()){
  78. int u=q.front();
  79. q.pop();
  80. for(int i=head[u];i;i=e[i].nxt){
  81. int v=e[i].v,w=e[i].w;
  82. if(w&&!dep[v]){
  83. dep[v]=dep[u]+1;
  84. q.push(v);
  85. if(v==T){
  86. return 1;
  87. }
  88. }
  89. }
  90. }
  91. return 0;
  92. }
  93. int dfs(int u,int flow,int T){
  94. if(u==T){
  95. return flow;
  96. }
  97. int s=0;
  98. for(int i=cur[u];i;i=e[i].nxt){
  99. cur[u]=i;
  100. int v=e[i].v,w=e[i].w;
  101. if(w&&dep[v]==dep[u]+1){
  102. int res=dfs(v,min(flow,w),T);
  103. e[i].w-=res;
  104. e[i^1].w+=res;
  105. flow-=res;
  106. s+=res;
  107. }
  108. if(!flow){
  109. break;
  110. }
  111. }
  112. if(!s){
  113. dep[u]=0;
  114. }
  115. return s;
  116. }
  117. void init(){
  118. for(int i=0;i<=tot;i+=2){
  119. e[i].w+=e[i^1].w;
  120. e[i^1].w=0;
  121. }
  122. }
  123. int dinic(int S,int T){
  124. init();
  125. int ans=0;
  126. while(bfs(S,T)){
  127. memcpy(cur,head,sizeof(head));
  128. ans+=dfs(S,inf,T);
  129. }
  130. return ans;
  131. }
  132. void build(int l,int r){
  133. if(l>=r){
  134. return;
  135. }
  136. int x=id[l],y=id[l+1];
  137. int flow=dinic(x,y);
  138. add1(x,y,flow);
  139. add1(y,x,flow);
  140. int cnt1=0,cnt2=0;
  141. for(int i=l;i<=r;i++){
  142. if(dep[id[i]]){
  143. tmp1[++cnt1]=id[i];
  144. }
  145. else{
  146. tmp2[++cnt2]=id[i];
  147. }
  148. }
  149. for(int i=1;i<=cnt1;i++){
  150. id[l+i-1]=tmp1[i];
  151. }
  152. for(int i=1;i<=cnt2;i++){
  153. id[cnt1+i+l-1]=tmp2[i];
  154. }
  155. build(l,l+cnt1-1);
  156. build(l+cnt1,r);
  157. }
  158. }
  159. bool vis[M];
  160. vector<int>s;
  161. void get(int u,int fa){
  162. s.pb(u);
  163. for(int i=hd[u];i;i=G[i].nxt){
  164. int v=G[i].v;
  165. if(vis[i]||v==fa){
  166. continue;
  167. }
  168. get(v,u);
  169. }
  170. }
  171. void dfs(int u){
  172. if(s.size()==1){
  173. write_space(s[0]);
  174. return;
  175. }
  176. int id=0,mn=inf;
  177. for(auto x:s){
  178. for(int i=hd[x];i;i=G[i].nxt){
  179. if(vis[i]){
  180. continue;
  181. }
  182. int w=G[i].w;
  183. if(w<mn){
  184. mn=w;
  185. id=i;
  186. }
  187. }
  188. }
  189. cerr<<id/2<<endl;
  190. vis[id]=vis[id^1]=1;
  191. vector<int>().swap(s);
  192. get(G[id].u,0);
  193. dfs(G[id].u);
  194. vector<int>().swap(s);
  195. get(G[id].v,0);
  196. dfs(G[id].v);
  197. }
  198. void solve(){
  199. read(n),read(m);
  200. for(int i=1,u,v,w;i<=m;i++){
  201. read(u),read(v),read(w);
  202. add(u,v,w);
  203. add(v,u,0);
  204. add(v,u,w);
  205. add(u,v,0);
  206. }
  207. for(int i=1;i<=n;i++){
  208. id[i]=i;
  209. }
  210. Gusfield::build(1,n);
  211. write_endl(ans>>1);
  212. for(int i=1;i<=n;i++){
  213. s.pb(i);
  214. }
  215. dfs(1);
  216. }
  217. signed main(){
  218. #ifndef ONLINE_JUDGE
  219. freopen("1.in","r",stdin);
  220. freopen("1.out","w",stdout);
  221. #endif
  222. int t=1;
  223. while(t--){
  224. solve();
  225. }
  226. return 0;
  227. }

Gusfield算法学习的更多相关文章

  1. DSP算法学习-过采样技术

    DSP算法学习-过采样技术 彭会锋 2015-04-27 23:23:47 参考论文: 1 http://wr.lib.tsinghua.edu.cn/sites/default/files/1207 ...

  2. 算法学习之C语言基础

    算法学习,先熟悉一下C语言哈!!! #include <conio.h> #include<stdio.h> int main(){ printf(+); getch(); ; ...

  3. Python之路,Day21 - 常用算法学习

    Python之路,Day21 - 常用算法学习   本节内容 算法定义 时间复杂度 空间复杂度 常用算法实例 1.算法定义 算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的 ...

  4. C / C++算法学习笔记(8)-SHELL排序

    原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...

  5. 算法学习之BFS、DFS入门

    算法学习之BFS.DFS入门 0x1 问题描述 迷宫的最短路径 给定一个大小为N*M的迷宫.迷宫由通道和墙壁组成,每一步可以向相邻的上下左右四格的通道移动.请求出从起点到终点所需的最小步数.如果不能到 ...

  6. 二次剩余Cipolla算法学习笔记

    对于同余式 \[x^2 \equiv n \pmod p\] 若对于给定的\(n, P\),存在\(x\)满足上面的式子,则乘\(n\)在模\(p\)意义下是二次剩余,否则为非二次剩余 我们需要计算的 ...

  7. Manacher算法学习笔记 | LeetCode#5

    Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...

  8. 第四百一十五节,python常用排序算法学习

    第四百一十五节,python常用排序算法学习 常用排序 名称 复杂度 说明 备注 冒泡排序Bubble Sort O(N*N) 将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮 ...

  9. PCA算法学习(Matlab实现)

    PCA(主成分分析)算法,主要用于数据降维,保留了数据集中对方差贡献最大的若干个特征来达到简化数据集的目的. 实现数据降维的步骤: 1.将原始数据中的每一个样本用向量表示,把所有样本组合起来构成一个矩 ...

  10. Javascript经典算法学习1:产生随机数组的辅助类

    辅助类 在几个经典排序算法学习部分,为方便统一测试不同算法,新建了一个辅助类,主要功能为:产生指定长度的随机数组,提供打印输出数组,交换两个元素等功能,代码如下: function ArraySort ...

随机推荐

  1. linux软件安装篇

    nginx篇 第一件事情 cd /etc/yum.repo.d mv CentOS-Base.repo CentOS-Base.repo.bak wget -O CentOS-Base.repo ht ...

  2. K8S二进制单节点部署

    一.常见的k8s部署方式 1.inikube: Minikube是一个工具,可以在本地快速运行一个单节点微型K8s,仅用于学习预览K8s的一些特性使用 部署地址: https://kubernetes ...

  3. Codeforces Round #803 (Div. 2) A-D 刚vp完还没补题

    Codeforces Round #803 (Div. 2) 2022/7/24 上午VP 传送门:https://codeforces.com/contest/1698 A. XOR Mixup 随 ...

  4. shader graph 制作的双面shader

  5. elementui中对样式的修改标签

    /deep/ .el-drawer.rtl { -webkit-animation: rtl-drawer-out .3s; animation: rtl-drawer-out .3s; backgr ...

  6. 【LeetCode回溯算法#07】子集问题I+II,巩固解题模板并详解回溯算法中的去重问题

    子集 力扣题目链接 给你一个整数数组 nums ,数组中的元素 互不相同 .返回该数组所有可能的子集(幂集). 解集 不能 包含重复的子集.你可以按 任意顺序 返回解集. 示例 1: 输入:nums ...

  7. Docker教程、架构、资源

    一.Docker教程 ​ Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源.Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中 ...

  8. 如何在mac上使用idea做大数据开发

    1 软件包下载 jdk1.8 (jdk1.8.0_231) idea(包括2018,2019) (ideaIC-2019.3.3/ideaIC-2018.3.5) 汉化包:汉化包.rar maven3 ...

  9. CAS 单点登录系统

    一.什么是单点登录 单点登录(Sign Sion On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一.SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系 ...

  10. redis.clients.jedis.exceptions.JedisConnectionException: Failed connecting to "xxxxx"

    Java 连接 Redis所遇问题 1. 检查Linux是否关闭防火墙,或对外开放redis默认端口6379 关闭防火墙. systemctl stop firewalld 对外开放端口.firewa ...