[学习笔记]最小割树
用于求任意两个点的最小割(最大流)
最小割树建造方法:
类似于分治
在当前点集中选择任意两个点作为源点、汇点,跑最小割G
两个点之间连树边,边权为G
把当前点集中和源点联通的能到的放进一个集合里,与T联通的放进另一个集合里。然后分治下去
注意,每次跑最小割G是在全局跑。
n次最小割
建出树来之后,任意两点的最小割,就是两点树上路径的边权最小值。
证明不知。
https://blog.csdn.net/qq_33229466/article/details/53290996
可以证明最小是这个最小值,但是感觉不能证明能取到=。
代码:(找联通块的话,用dfs染色即可)
#include<bits/stdc++.h> #define il inline #define reg register int #define numb (ch^'0') using namespace std; typedef long long ll; il void rd(int &x){ char ch;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=505; const int M=15050; const int inf=0x3f3f3f3f; int n,m,s,t; struct node{ int nxt,to; int w; }e[2*M],bian[2*M]; struct bbbb{ int x,y,z; }bb[M]; int hd[N],cnt=1; void add(int x,int y,int z){ e[++cnt].nxt=hd[x]; e[cnt].to=y; e[cnt].w=z; hd[x]=cnt; e[++cnt].nxt=hd[y]; e[cnt].to=x; e[cnt].w=z; hd[y]=cnt; } int pre[N],tot; void add_c(int x,int y,int z){ bian[++tot].nxt=pre[x]; bian[tot].to=y; bian[tot].w=z; pre[x]=tot; } int d[N]; int dfs(int x,int flow){ int res=flow; if(x==t) return flow; for(reg i=hd[x];i&&res;i=e[i].nxt){ int y=e[i].to; if(e[i].w&&d[y]==d[x]+1){ int k=dfs(y,min(e[i].w,res)); if(!k) d[y]=0; res-=k; e[i].w-=k; e[i^1].w+=k; } } return flow-res; } int q[N],l,r; bool bfs(){ l=1,r=0; memset(d,0,sizeof d); d[s]=1; q[++r]=s; while(l<=r){ int x=q[l++]; //cout<<" bfs "<<x<<endl; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(!d[y]&&e[i].w){ d[y]=d[x]+1; q[++r]=y; if(y==t) return true; } } } return false; } int dinic(int x,int y){ s=x,t=y; memset(hd,0,sizeof hd); cnt=1; for(reg i=1;i<=m;++i){ add(bb[i].x,bb[i].y,bb[i].z); } int maxflow=0; int flow=0; while(bfs()){ while(flow=dfs(s,inf)) maxflow+=flow; } return maxflow; } int ptr; int mem[N],b[N],co[N]; bool in[N]; void fin(int x){ co[x]=ptr; for(reg i=hd[x];i;i=e[i].nxt){ if(e[i].w&&co[e[i].to]!=ptr) fin(e[i].to); } } void divi(int l,int r){ if(l==r) return; int ge=dinic(mem[l],mem[r]); add_c(mem[l],mem[r],ge); add_c(mem[r],mem[l],ge); ++ptr; fin(mem[l]); int pp=l-1,qq=r+1; for(reg i=l;i<=r;++i){ if(co[mem[i]]==ptr) b[++pp]=mem[i]; else b[--qq]=mem[i]; } for(reg i=l;i<=r;++i){ mem[i]=b[i]; } divi(l,pp);divi(qq,r); } int fa[N][13],mi[N][13]; int dep[N]; void dfs2(int x,int deep){ dep[x]=deep; for(reg i=pre[x];i;i=bian[i].nxt){ int y=bian[i].to; if(y==fa[x][0]) continue; fa[y][0]=x; mi[y][0]=bian[i].w; dfs2(y,deep+1); } } int slo(int x,int y){ int ans=inf; if(dep[x]<dep[y]) swap(x,y); for(reg i=12;i>=0;--i){ if(dep[fa[x][i]]>=dep[y]) { ans=min(ans,mi[x][i]),x=fa[x][i]; } } if(x==y) return ans; for(reg i=12;i>=0;--i){ if(fa[x][i]!=fa[y][i]) { ans=min(ans,mi[x][i]); ans=min(ans,mi[y][i]); x=fa[x][i],y=fa[y][i]; } } ans=min(ans,min(mi[x][0],mi[y][0])); return ans; } int main(){ rd(n);rd(m); int x,y,z; for(reg i=1;i<=m;++i){ rd(x);rd(y);rd(z); //add(x,y,z); bb[i].x=x,bb[i].y=y;bb[i].z=z; } for(reg i=1;i<=n;++i) mem[i]=i; divi(1,n); dfs2(1,1); for(reg j=1;j<=12;++j){ for(reg i=1;i<=n;++i){ fa[i][j]=fa[fa[i][j-1]][j-1]; mi[i][j]=min(mi[i][j-1],mi[fa[i][j-1]][j-1]); } } int T; rd(T); dep[0]=-1; while(T--){ rd(x);rd(y); printf("%d\n",slo(x,y)); } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/12/16 22:45:00 */
还有两个模板题;[ZJOI2011]最小割 [CQOI2016]不同的最小割