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

最小割树

题目大意:

给定一张无向图,每条边带边权。

每次询问给定两个点,求它们的最小割。

题解:

最小割树。

对于一棵最小割树上的一条边 $(u,v)$,其边权表示原图中 $u,v$ 的最小割,并且这个最小割分成的两个集合中的点,恰被该树边分成两棵不同的子树。

而对于图上两点之间的最小割,其值等于最小割树上两点的简单路径上的最小的边权。

我们根据这个定义构造最小割树:

每次从当前点集中任选两个点 $S,T$,作为源点和汇点,并跑最小割(在完整的原图中)。

求出最小割的值,在树上连接 $S,T$,边权为最小割大小。

然后,我们 dfs 一遍,得到当前点集被最小割分成的两个点集。

分别对两个点集继续进行上述步骤,直到点集中只有 $1$ 个元素为止。

然后就可以方便地处理询问了。

本题 $n$ 较小,所以查询时可以直接在树上暴力。

Code:

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int N=505;
int n,head[N],cnt,m;
bool inq[N];
struct edge{
    int to,nxt,cap;
}e[N*10];
namespace tr{
    edge e[N<<1];
    int head[N],cnt,dep[N],fa[N],dis[N];
    inline void addedge(int u,int v,int w){
        e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
        e[++cnt]=(edge){u,head[v],w},head[v]=cnt;
    }
    void dfs(int now){
        for(int i=head[now];i;i=e[i].nxt)
        if(!dep[e[i].to]){
            dep[e[i].to]=dep[now]+1;
            dis[e[i].to]=e[i].cap;
            fa[e[i].to]=now;
            dfs(e[i].to);
        }
    }
    void init(){
        dep[0]=1;
        dfs(0);
    }
    inline int ask(int x,int y){
        if(dep[x]<dep[y])swap(x,y);
        int ret=0x3f3f3f3f;
        while(dep[x]>dep[y])ret=min(ret,dis[x]),x=fa[x];
        while(x!=y)ret=min(ret,min(dis[x],dis[y])),x=fa[x],y=fa[y];
        return ret;
    }
}
namespace MF{
    int S,T;
    edge e[N*10];
    int head[N],cnt,dep[N],iter[N];
    bool vis[N];
    queue<int>q;
    void bfs(){
        dep[S]=1;
        for(q.push(S);!q.empty();){
            int u=q.front();q.pop();
            for(int i=head[u];i;i=e[i].nxt)
            if(e[i].cap&&!dep[e[i].to]){
                dep[e[i].to]=dep[u]+1;
                q.push(e[i].to);
            }
        }
    }
    int dfs(int u,int f){
        if(u==T||!f)return f;
        for(int&i=iter[u];i;i=e[i].nxt)
        if(e[i].cap&&dep[e[i].to]>dep[u]){
            int d=dfs(e[i].to,min(f,e[i].cap));
            if(d){
                e[i].cap-=d,e[i^1].cap+=d;
                return d;
            }
        }
        return 0;
    }
    int dinic(){
        for(int flow=0;;){
            for(int i=0;i<=n;++i)dep[i]=0,iter[i]=head[i];
            bfs();
            if(dep[T]==0)return flow;
            for(int f;f=dfs(S,0x3f3f3f3f);flow+=f);
        }
    }
    void DFS(int u){
        vis[u]=1;
        for(int i=head[u];i;i=e[i].nxt)
        if(e[i].cap&&!vis[e[i].to])DFS(e[i].to);
    }
    int maxflow(vector<int>&vq,int u,int v,vector<int>&L,vector<int>&R){
        S=u,T=v;
        cnt=1;
        for(int i=0;i<=n;++i)head[i]=0;
        for(int now=0;now<=n;++now){
            for(int i=::head[now];i;i=::e[i].nxt)
            if(now< ::e[i].to){
                e[++cnt]=(edge){::e[i].to,head[now],::e[i].cap},head[now]=cnt,
                e[++cnt]=(edge){now,head[::e[i].to],::e[i].cap},head[::e[i].to]=cnt;
            }
        }
        int ret=dinic();
        for(int i=0;i<=n;++i)vis[i]=0;
        DFS(S);
        for(int i=0;i<vq.size();++i)
        if(vis[vq[i]])L.push_back(vq[i]);else R.push_back(vq[i]);
        return ret;
    }
}
vector<int>vq;
void solve(vector<int>&vq){
    if(vq.size()==1)return;
    for(int i=0;i<vq.size();++i)inq[vq[i]]=1;
    int u=vq.front(),v=vq.back();
    vector<int>L,R;
    tr::addedge(u,v,MF::maxflow(vq,u,v,L,R));
    for(int i=0;i<vq.size();++i)inq[vq[i]]=0;
    solve(L);
    solve(R);
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=m;++i){
        int u,v,w;
        cin>>u>>v>>w;
        e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
        e[++cnt]=(edge){u,head[v],w},head[v]=cnt;
    }
    for(int i=0;i<=n;++i)vq.push_back(i);
    solve(vq);
    tr::init();
    int t;
    for(cin>>t;t--;){
        int x,y;
        cin>>x>>y;
        cout<<tr::ask(x,y)<<'\n';
    }
    return 0;
}