luogu P4897 【模板】最小割树(Gomory-Hu Tree)
题面传送门
看到一道题口胡出最小割树的做法了但是不会写。于是赶紧来学习一下。
首先最小割树的定义是:有这么一棵树,树上的边\((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));
}