最小割树(Gomory–Hu Tree)

最小割树有这样的性质:对于原无向图任意两点 \(u,v\),其最小割为最小割树中 \(u-v\) 路径上边权的最小值.

只需要跑 \(n-1\) 次最大流即可构建最小割树(Gomory-Hu 算法),具体如下:

  • 维护 \(fa\)\(W\) 数组,分别表示父节点和边权,钦定根为 \(1\),初始所有节点指向根节点.

  • 顺序遍历 \(2\to n\),令当前节点为 \(s\)\(fa_s = t\),求出 \(s-t\) 的最小割,令 \(W_s\) 为这个值.

  • 每一轮跑完 dinic,将残余网络中属于 \(S\) 集、节点编号大于 \(s\)、且父节点为 \(t\) 的节点指向 \(s\).

  • 另外,若 \(fa_t\) 也在残余网络的 \(S\) 集中,交换 \(s,t\).

时间复杂度 \(\mathcal{O}(nF)\)\(F\) 是原图的最大流.

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct dinic{
    int n,s,t;
    ll mxf;
    const ll INF = 9e18;
    vector<int> cur,depth;
    vector<vector<array<ll,4>>> adj;

    dinic(int _n,int _s,int _t){
        n = _n;
        s = _s;
        t = _t;
        cur.resize(n+1);
        depth.resize(n+1);
        adj.resize(n+1);
    }

    void add(int u,int v,ll cap){
        adj[u].push_back({cap,0,(int)adj[v].size(),v});
        adj[v].push_back({0,1,(int)adj[u].size()-1,u});
    }

    bool bfs(){
        fill(depth.begin()+1,depth.end(),-1);
        depth[s] = 0;
        queue<int> q;
        q.push(s);

        while (!q.empty()){
            int u = q.front();
            q.pop();

            for (auto& [cap,flag,rev,v]:adj[u]){
                if (cap && depth[v]==-1){
                    depth[v] = depth[u]+1;
                    q.push(v);
                }
            }
        }

        return depth[t]!=-1;
    }

    ll dfs(int u,ll mf){
        if (u==t) return mf;
        int len = adj[u].size();
        ll sum = 0;

        for (int& i=cur[u];i<len;i++){
            auto& [cap,flag,rev,v] = adj[u][i];
            if (cap && depth[v]==depth[u]+1){
                ll f = dfs(v,min(cap,mf));
                cap -= f;
                mf -= f;
                sum += f;
                adj[v][rev][0] += f; 
            }
            if (mf==0) break;
        }

        if (sum==0) depth[u] = -1;
        return sum;
    }

    void work(){
        mxf = 0;
        while (bfs()){
            fill(cur.begin(),cur.end(),0);
            mxf += dfs(s,INF);
        }
    }
};

const ll INF = 9e18;

void solve(){
    int n,m;
    cin >> n >> m;

    vector<vector<pair<int,int>>> adj(n+1);
    for (int i=0;i<m;i++){
        int u,v,w;
        cin >> u >> v >> w;
        adj[u].emplace_back(w,v);
        adj[v].emplace_back(w,u);
    }

    vector<int> fa(n+1,1);
    fa[1] = -1;
    vector<ll> W(n+1);

    for (int s=2;s<=n;s++){
        int t = fa[s];
        dinic dn(n,s,t);

        for (int u=1;u<=n;u++){
            for (auto& [w,v]:adj[u]){
                dn.add(u,v,w);
            }
        }

        dn.work();
        W[s] = dn.mxf;

        for (int i=s+1;i<=n;i++){
            if (dn.depth[i]!=-1 && fa[i]==t){
                fa[i] = s;
            }
        } 

        if (fa[t]!=-1 && dn.depth[fa[t]]!=-1){
            fa[s] = fa[t];
            fa[t] = s;
            swap(W[s],W[t]);           
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int t = 1;
    cin >> t;
    while (t--) solve();

    return 0;
}
posted @ 2026-08-28 09:31  kzssCCC  阅读(1)  评论(0)    收藏  举报