• 最小割树


    最小割树(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();
    }
    
  • 相关阅读:
    「模板」 树套树
    [Luogu 3701] 「伪模板」主席树
    「模板」 可持久化平衡树
    「模板」 割点
    [Luogu 2596] ZJOI2006 书架
    省选有感。(不是游记)
    [Luogu 2604] ZJOI2010 网络扩容
    MySql聚簇索引与非聚簇索引的区别
    四层负载均衡和七层负载均衡的区别
    Redis利用Pipeline加速查询速度的方法
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12849015.html
Copyright © 2020-2023  润新知