• [学习笔记]最小割树(Gomory-Hu Tree)


    最小割树((mathcal{Gomory-Hu Tree}))简明指南

    对于单源最短路径,我们有(SPFA)(Dijkstra),对于多源最短路径,我们有(Floyd);对于两点间的最小割,我们有(Dinic)(ISAP),那么对于多组最小割的询问呢?

    这是板子题:

    P4897 【模板】最小割树(Gomory-Hu Tree)

    这是教程:

    首先有一个定理,就是一个(n)个点的图上,两点之间只有(n)种本质不同的最小割。因此一定存在一棵树,满足树上两点的最小割等于原图上两点的最小割。我们把这样的树称之为“最小割树”。 --Ebola

    下面考虑如何建出这样的一棵树。

    首先我们在图中随意选取两个点(u,v),跑出他们之间的最小割(cut(u,v))。那么满流的边就是图中(u,v)之间的割。通过这条割我们把图划分成(u)所在的部分和(v)所在的部分。把(u)所在的那一部分的点集记作(U)(v)所在的那一部分的点集记作(V)

    • 引理:对于任意(x in U,y in V),有(cut(x,y) leq cut(u,v))
    • 证明:假设有(cut(x,y) > cut(u,v)),那么(cut(u,v))就不能把(u,v)割开,因为(x,y)依然相连。

    那么我们在一张你没有玩过的船新图上在(u,v)之间建一条值为(cut(u,v))的边。接下来,我们再分别对(U,V)集合进行上述的相同操作,也就是在每一集合中找两个点求最小割,再把集合割开成两个...这样,当我们把原图的点的大集合全部割成一个个点集之后,刚好跑了(n)次网络流,建了(n-1)条边,也就建成了一棵(Gomory)-(Hu Tree)

    • 引理:对于树上的两点(u,v),他们的最小割是他们之间简单路径的最小路径权值。
    • 根据上一条引理,显然可得。

    那么我们只需要在树上进行查询们就可以得到任意两点的最小割。可以用倍增,也可以用树链剖分。

    这是板子:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,node[505],dep[505],fa[505][10],mn[505][10];
    int cnt,top[505],to[1005],len[1005],nex[1005];
    int read()
    {
        int re=0;
        char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
        return re;
    }
    void add_edge(int x,int y,int z)
    {
        to[++cnt]=y,len[cnt]=z,nex[cnt]=top[x],top[x]=cnt;
        to[++cnt]=x,len[cnt]=z,nex[cnt]=top[y],top[y]=cnt;
    }
    namespace GHT
    {
        int s,t;
        int tot,cur[505],dep[505],col[505],col_bucket[505];
        int cnt=1,top[505],to[3005],cap[3005],flow[3005],nex[3005];
        void add_edge(int x,int y,int z)
        {
            to[++cnt]=y,cap[cnt]=z,flow[cnt]=0,nex[cnt]=top[x],top[x]=cnt;
            to[++cnt]=x,cap[cnt]=z,flow[cnt]=0,nex[cnt]=top[y],top[y]=cnt;
        }
        bool BFS()
        {
            memset(cur,0,sizeof cur);
            memset(dep,0,sizeof dep);
            dep[s]=1,cur[s]=top[s];
            queue<int>Q;
            Q.push(s);
            while(!Q.empty())
            {
                int now=Q.front();Q.pop();
                for(int i=top[now];i;i=nex[i])
                    if(!dep[to[i]]&&cap[i]>flow[i])
                    {
                        dep[to[i]]=dep[now]+1;
                        cur[to[i]]=top[to[i]];
                        Q.push(to[i]);
                    }
            }
            return dep[t]!=0;
        }
        int DFS(int now,int rest)
        {
            if(now==t) return rest;
            int re=0;
            for(int &i=cur[now];i;i=nex[i])
                if(dep[to[i]]==dep[now]+1&&cap[i]>flow[i])
                {
                    int lzq=DFS(to[i],min(rest,cap[i]-flow[i]));
                    if(lzq)
                    {
                        rest-=lzq,re+=lzq;
                        flow[i]+=lzq,flow[i^1]-=lzq;
                        if(!rest) break;
                    }
                }
            return re;
        }
        int Dinic(int x,int y)
        {
            int re=0;s=x,t=y;
            for(int i=1;i<=cnt;i++) flow[i]=0;
            while(BFS()) re+=DFS(s,0x3f3f3f3f);
            return re;
        }
        void get_color(int now,int color)
        {
            col[now]=color;
            for(int i=top[now];i;i=nex[i])
                if(cap[i]>flow[i]&&col[to[i]]!=color)
                    get_color(to[i],color);
        }
        void build(int l,int r)
        {
            if(l==r) return ;
            int x=node[l],y=node[l+1];
            int cut=Dinic(x,y);
            get_color(x,++tot);
            int L=l,R=r;
            for(int i=l;i<=r;i++)
                if(col[node[i]]==tot) col_bucket[L++]=node[i];
                else col_bucket[R--]=node[i];
            for(int i=l;i<=r;i++) node[i]=col_bucket[i];
            ::add_edge(x,y,cut);
            build(l,L-1);
            build(R+1,r);
        }
    }
    void dfs(int now)
    {
        for(int i=1;i<=9;i++)
        {
            fa[now][i]=fa[fa[now][i-1]][i-1];
            mn[now][i]=min(mn[now][i-1],mn[fa[now][i-1]][i-1]);
        }
        for(int i=top[now];i;i=nex[i])
        {
            if(to[i]==fa[now][0]) continue;
            dep[to[i]]=dep[now]+1,fa[to[i]][0]=now,mn[to[i]][0]=len[i];
            dfs(to[i]);
        }
    }
    int getcut(int x,int y)
    {
        int re=INT_MAX;
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=9;i>=0;i--) if(dep[fa[x][i]]>=dep[y]) re=min(re,mn[x][i]),x=fa[x][i];
        if(x==y) return re;
        for(int i=9;i>=0;i--) if(fa[x][i]!=fa[y][i]) re=min(re,min(mn[x][i],mn[y][i])),x=fa[x][i],y=fa[y][i];
        return min(re,min(mn[x][0],mn[y][0]));
    }
    int main()
    {
        n=read(),m=read();
        while(m--)
        {
            int x=read(),y=read(),z=read();
            GHT::add_edge(x,y,z);
        }
        for(int i=1;i<=n;i++) node[i]=i;
        GHT::build(1,n);
        dep[1]=1;
        dfs(1);
        m=read();
        while(m--)
        {
            int x=read(),y=read();
            printf("%d
    ",getcut(x,y));
        }
        return 0;
    }
    
  • 相关阅读:
    MySQL Case When 用法
    Delphi磁性窗口
    一个灵巧的Delphi多播实事件现方案.
    Delphi bpl 插件框架
    Win7下超级管理员创建普通权限任务
    Delphi 插件(Plugins)创建、调试与使用应用程序扩展
    Dll中导出类Delphi实战
    让你的程序支持插件
    构造一个通用的回调Thunk.(把回调函数指向对象的方法的办法)
    打造类.NET带垃圾回收功能的Delphi版GDIPlus
  • 原文地址:https://www.cnblogs.com/coder-Uranus/p/9771919.html
Copyright © 2020-2023  润新知