最小割树(Gomory-Hu Tree)
定义
给定一个无向图(G=(V,E)),定义其最小割树(T=(V,E'))为满足:
(forall (u,v,w)in E',w=C(G,u,v)),且(Tsetminus{(u,v,w)})的两个点集恰好是(G)上(Cut(G,u,v))分割出的两个点集。
构造
在当前集合任选两个点(u,v),添加树边((u,v,C(G,u,v))),然后找到(G)上(Cut(G,u,v))分割出的两个点集递归构造。
时间复杂度为(O(n*operatorname{maxflow}(n,m)))。
性质
(C(G,u,v)=minlimits_{ein p(u,v)}w(e))
那么我们可以先求出最小割树的边集,再建出最小割树的Kruskal重构树,那么最小割树上两点间的最小边权就是Kruskal重构树上两点的(operatorname{lca})的权值。
#include<queue>
#include<cctype>
#include<cstdio>
#include<vector>
#include<cstring>
#include<numeric>
#include<utility>
#include<algorithm>
using pi=std::pair<int,int>;
const int N=507,inf=1e9;
int n,m;
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
struct Dinic
{
int tot=1,head[N],cur[N],dep[N];struct edge{int u,v,f,next;}e[6007];
void add(int u,int v,int f){e[++tot]={u,v,f,head[u]},head[u]=tot,e[++tot]={v,u,0,head[v]},head[v]=tot;}
void undo(){for(int i=2;i<=tot;i+=2)e[i].f+=e[i^1].f,e[i^1].f=0;}
int bfs(int s,int t)
{
static std::queue<int>q;
memset(dep+1,0,4*n),memcpy(cur+1,head+1,4*n),dep[s]=1,q.push(s);
for(int i,u,v;!q.empty();) for(u=q.front(),q.pop(),i=head[u];i;i=e[i].next) if(!dep[v=e[i].v]&&e[i].f) dep[v]=dep[u]+1,q.push(v);
return dep[t];
}
int dfs(int u,int t,int lim)
{
if(!lim||u==t) return lim;
int v,flow=0;
for(int&i=cur[u],f;i;i=e[i].next)
if(dep[v=e[i].v]==dep[u]+1&&(f=dfs(v,t,std::min(lim,e[i].f))))
{
flow+=f,lim-=f,e[i].f-=f,e[i^1].f+=f;
if(!lim) break;
}
return flow;
}
int flow(int s,int t)
{
undo();int ans=0;
while(bfs(s,t)) ans+=dfs(s,t,inf);
return ans;
}
void build(){for(int i=1,u,v,w;i<=m;++i)u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w);}
}g;
struct Gomory_Hu_Tree
{
int cnt,id[N],tmp[N];struct edge{int u,v,w;}e[N];
void build(int l,int r)
{
if(l==r) return;
int s=id[l],t=id[l+1],c=g.flow(s,t),L=l,R=r;
for(int i=l;i<=r;++i) (g.dep[id[i]]? tmp[L++]:tmp[R--])=id[i];
e[++cnt]={s,t,c},memcpy(id+l,tmp+l,4*(r-l+1)),build(l,L-1),build(R+1,r);
}
void build(){std::iota(id+1,id+n+1,1),build(1,n),std::sort(e+1,e+n,[](const edge&a,const edge&b){return a.w>b.w;});}
}t1;
struct Disjoint_Set
{
int fa[2*N];
int find(int x){return x==fa[x]? x:fa[x]=find(fa[x]);}
void build(){std::iota(fa+1,fa+n+n,1);}
}t2;
struct Kruskal_Tree
{
int root,val[2*N],fa[2*N],dep[2*N],size[2*N],son[2*N],top[2*N];std::vector<int>e[2*N];
void dfs1(int u)
{
size[u]=1,dep[u]=dep[fa[u]]+1;
for(int v:e[u]) if(fa[v]=u,dfs1(v),size[u]+=size[v],size[v]>size[son[u]]) son[u]=v;
}
void dfs2(int u,int tp)
{
top[u]=tp;
if(son[u]) dfs2(son[u],tp);
for(int v:e[u]) if(v^son[u]) dfs2(v,v);
}
int lca(int u,int v)
{
for(;top[u]^top[v];u=fa[top[u]]) if(dep[top[u]]<dep[top[v]]) std::swap(u,v);
return dep[u]<dep[v]? u:v;
}
void build()
{
for(int i=1,u,v;i<n;++i) val[n+i]=t1.e[i].w,u=t2.find(t1.e[i].u),v=t2.find(t1.e[i].v),fa[u]=fa[v]=t2.fa[u]=t2.fa[v]=n+i;
for(int i=1;i<n+n-1;++i) e[fa[i]].push_back(i);
dfs1(root=n+n-1),dfs2(root,root);
}
void solve(){printf("%d
",val[lca(read(),read())]);}
}t3;
int main()
{
n=read(),m=read();
g.build(),t1.build(),t2.build(),t3.build();
for(int q=read();q;--q) t3.solve();
}