luogu P4897 【模板】最小割樹(Gomory-Hu Tree)
阿新 • • 發佈:2021-06-15
題面傳送門
看到一道題口胡出最小割樹的做法了但是不會寫。於是趕緊來學習一下。
首先最小割樹的定義是:有這麼一棵樹,樹上的邊\((x,y)\)的權值為原圖\(x,y\)之間的最小割的值。並且將這顆樹的這條邊斷開劃分出的兩個點集是原圖斷開最小割的兩個點集。
那麼依據這個東西建樹,先隨便找到兩個點,求出最小割,劃分出點集,然後遞迴計算即可。
這個東西用\(dicnic\)實現,複雜度\(O(n^3m)\)
然後最小割樹有一個奇妙的性質,就是樹上兩點間的最小邊權是原圖兩點的最小割。
那麼倍增查詢即可。時間複雜度\(O(qlogn)\)
code:
#include <vector> #include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<cmath> #include<algorithm> #include<bitset> #include<set> #include<map> #define I inline #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define abs(x) ((x)>0?(x):-(x)) #define re register #define ll long long #define db double #define N 500 #define eps (1e-5) #define mod (1<<31) #define U unsigned int using namespace std; int n,m,d[N+5],nows[N+5],A[N+5],fa[N+5][10],F[N+5][10],S,T,lg[N+5],now,Q,x,y,z,B[N+5],Bh,C[N+5],Ch; struct yyy{int to,w,z;}tmp; struct ljb{ int head,h[N+5];yyy f[N+5<<4]; I void add(int x,int y,int z){ f[head]=(yyy){y,z,h[x]};h[x]=head++;} }s,G,Gs;queue<int> q; I void get(int x,int y,int z){s.add(x,y,z);s.add(y,x,0);} I int bfs(){ while(!q.empty()) q.pop();memset(d,0x3f,sizeof(d));q.push(S);d[S]=0;nows[S]=s.h[S]; while(!q.empty()){ now=q.front();q.pop();for(int i=s.h[now];~i;i=tmp.z){ tmp=s.f[i];if(!tmp.w||d[tmp.to]<1e9) continue;d[tmp.to]=d[now]+1; nows[tmp.to]=s.h[tmp.to];q.push(tmp.to);if(tmp.to==T) return 1; } } return 0; } I int dfs(int x,int sum){ if(x==T) return sum;yyy tmp;int i,pus=0,k;for(i=nows[x];~i;i=tmp.z){ tmp=s.f[i];nows[x]=i;if(!tmp.w||d[tmp.to]!=d[x]+1) continue; k=dfs(tmp.to,min(tmp.w,sum));if(!k) d[tmp.to]=1e9; sum-=k;pus+=k;s.f[i].w-=k;s.f[i^1].w+=k;if(!sum) break; } return pus; } I void build(int l,int r){ if(l==r) return;re int i,Ans=0;S=A[l];T=A[r];s=G;while(bfs())Ans+=dfs(S,1e9);Gs.add(S,T,Ans);Gs.add(T,S,Ans); //printf("%d %d %d\n",Ans,S,T); Bh=Ch=0;for(i=l;i<=r;i++) (d[A[i]]<1e9?B[++Bh]:C[++Ch])=A[i];for(i=l;i<=l+Bh-1;i++) A[i]=B[i-l+1]; for(i=l+Bh;i<=r;i++) A[i]=C[i-Bh-l+1];Ans=Bh;build(l,l+Ans-1);build(l+Ans,r); } I void Make(int x,int last){ int i;d[x]=d[last]+1;yyy tmp;fa[x][0]=last;for(i=1;i<=lg[d[x]];i++) fa[x][i]=fa[fa[x][i-1]][i-1],F[x][i]=min(F[x][i-1],F[fa[x][i-1]][i-1]); for(i=s.h[x];~i;i=tmp.z) tmp=s.f[i],tmp.to^last&&(F[tmp.to][0]=tmp.w,Make(tmp.to,x),0); } I void swap(int &x,int &y){x^=y^=x^=y;} I int find(int x,int y){ d[x]<d[y]&&(swap(x,y),0);int ans=1e9;while(d[x]^d[y])ans=min(ans,F[x][lg[d[x]-d[y]]]),x=fa[x][lg[d[x]-d[y]]];//printf("%d %d %d\n",ans,x,y); if(x==y) return ans;for(int i=lg[d[x]];~i;i--) fa[x][i]^fa[y][i]&&(ans=min(ans,min(F[x][i],F[y][i])),x=fa[x][i],y=fa[y][i]);return min(ans,min(F[x][0],F[y][0])); } int main(){ freopen("1.in","r",stdin);freopen("1.out","w",stdout); re int i,j;memset(s.h,-1,sizeof(s.h));memset(Gs.h,-1,sizeof(Gs.h));for(i=2;i<=n+1;i++)lg[i]=lg[i/2]+1; scanf("%d%d",&n,&m);for(i=1;i<=m;i++)scanf("%d%d%d",&x,&y,&z),get(x,y,z),get(y,x,z); G=s;for(i=1;i<=n;i++) A[i]=i;build(0,n);s=Gs;memset(d,0,sizeof(d));Make(0,0); scanf("%d",&Q);while(Q--) scanf("%d%d",&x,&y),printf("%d\n",find(x,y)); }