1. 程式人生 > >[學習筆記]最小割樹

[學習筆記]最小割樹

用於求任意兩個點的最小割(最大流)

Gomory-Hu tree (最小割樹) 介紹及實現 - jyxjyx27的專..._CSDN部落格

最小割樹建造方法:

類似於分治

在當前點集中選擇任意兩個點作為源點、匯點,跑最小割G

兩個點之間連樹邊,邊權為G

把當前點集中和源點聯通的能到的放進一個集合裡,與T聯通的放進另一個集合裡。然後分治下去

注意,每次跑最小割G是在全域性跑。

n次最小割

 

建出樹來之後,任意兩點的最小割,就是兩點樹上路徑的邊權最小值。

證明不知。

https://blog.csdn.net/qq_33229466/article/details/53290996

可以證明最小是這個最小值,但是感覺不能證明能取到=。

 

程式碼:(找聯通塊的話,用dfs染色即可)

【模板】最小割樹(Gomory-Hu Tree)

#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]不同的最小割