For an undirected graph there is a weighted tree on the same vertices such that for every pair the minimum – cut equals the minimum edge weight on the tree path, and removing that edge splits the vertices into an actual minimum – cut. It takes max flows (Gusfield’s version needs no contraction). Consequences: at most distinct pairwise min-cut values, all-pairs queries by path minimum, edge connectivity = lightest tree edge. No such tree exists for directed graphs.
vector<array<ll, 3>> gomoryHu(int n, const vector<array<ll, 3>>& E) { // {u, v, w}; returns tree edges {v, p, cut}
vector<int> p(n, 0); vector<ll> fl(n);
for (int s = 1; s < n; s++) {
int t = p[s]; Dinic D(n);
for (auto [u, v, w] : E) D.addEdge(u, v, w, w);
ll f = D.maxflow(s, t); fl[s] = f;
for (int i = 0; i < n; i++) if (i != s && D.sourceSide(i) && p[i] == t) p[i] = s;
if (D.sourceSide(p[t])) p[s] = p[t], p[t] = s, fl[s] = fl[t], fl[t] = f; // keeps the cut property
}
vector<array<ll, 3>> tree;
for (int v = 1; v < n; v++) tree.push_back({v, p[v], fl[v]});
return tree;
}Dropping the last line of the loop (the swap) and the i < s updates still gives a correct flow-equivalent tree (right values, but tree-edge cuts may not be the real cuts). Swap Dinic for push–relabel on dense graphs.
Using the tree
- Query : path minimum (binary lifting, or DFS per query for small ).
- Sum of over all pairs: sort tree edges by decreasing weight and merge with a DSU; an edge joining components of sizes contributes .
- Count pairs with : the same DSU, stopping at weight .
- Minimum -cut approximation: remove the lightest tree edges; within a factor .
- Minimum cut separating a vertex set (Steiner cut): the lightest tree edge whose removal splits .
ll allPairsMinCutSum(int n, vector<array<ll, 3>> tree) {
sort(all(tree), [](auto& a, auto& b) { return a[2] > b[2]; });
DSU d(n); ll s = 0;
for (auto [u, v, w] : tree) s += w * d.size(u) * d.size(v), d.unite(u, v);
return s;
}Proof: path minimum equals given the cut property
Triangle inequality: , because a minimum – cut separates from or from , and is then at least that pair’s minimum. Along any path this gives the minimum of over consecutive pairs. Cut property: every tree edge has weight , and the two components of the tree without it form a cut of exactly that weight. Then along the tree path, = min edge weight, and the lightest edge’s fundamental cut separates from with that weight, so it. Equality, and that cut is a minimum – cut. The DSU sum then follows: when the edge of weight merges components and , every earlier edge is at least , so all pairs across have path minimum exactly .
Proof sketch: why the construction keeps the cut property
Uncrossing lemma. Let be a minimum – cut () and . Then some minimum – cut lies inside . Let be any minimum – cut; replacing by its complement (and swapping ) we may assume . Cut functions are submodular: . contains but not , so , giving ; and still contains exactly one of . So contracting to one vertex does not change .
Gomory–Hu maintain a tree of super-vertices with the cut property. Splitting a super-vertex with : contract each subtree hanging off into one vertex (each lies on one side of a minimum cut of an earlier pair, so by the lemma is unchanged), find a minimum – cut , split into and , joined by an edge of weight , and attach each hanging subtree to the side its contracted vertex fell on. The old edges keep their cuts (the subtrees moved as wholes) and the new edge’s fundamental cut is . After splits every super-vertex is a single vertex.
Gusfield shows the contraction is unnecessary: by the lemma, a minimum – cut computed in the uncontracted graph can be uncrossed, without changing its value or how it splits , into one that keeps every hanging subtree on one side. The array stores the tree with each super-vertex represented by a star around one vertex; reassigning
p[i] = sfor the vertices of ‘s side, and the swap when the parent of falls on ‘s side, are exactly the split-and-reattach step expressed on that representation.