最小割树

最小割树(Gomory-Hu Tree)

定义

给定一个无向图\(G=(V,E)\),定义其最小割树\(T=(V,E')\)为满足:
\(\forall (u,v,w)\in E',w=C(G,u,v)\),且\(T\setminus\{(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)=\min\limits_{e\in 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\n",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();
}
posted @ 2020-05-08 10:31  Shiina_Mashiro  阅读(197)  评论(0编辑  收藏  举报