最小割树(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;
}

浙公网安备 33010602011771号