Definition
对于一张图(有向/无向),都能建立一棵Gomory-Hu Tree,树中节点对应图中节点,树上两点间的最小割等于图中两点间的最小割.
Algorithm
1.任取两个点s,t求出在整张图中的st最小割C,在最小割树中加入(s,t,|C|)的边.
2.在s和t的割集中分别递归操作.
3.在点集大小为1时停止操作.
显然操作次数是n-1,这样复杂度是O(n * Maxflow),大约是O(n^4).
Proof
一些性质
首先定义函数c(S)是有且仅有一个端点在点集S内的边的权值和函数.
在无向图中,该函数满足下列两个性质(虽然很多和该性质相关的问题是NP-Hard,但是针对c(S)的证明不难):
- submodularity(次模性/子模性)
如图:
c(A)=a+c+e+f,c(B)=b+c+d+f
c
(
A
)
=
a
+
c
+
e
+
f
,
c
(
B
)
=
b
+
c
+
d
+
f
c(A∪B)=a+b+c,c(A∩B)=c+d+e
c
(
A
∪
B
)
=
a
+
b
+
c
,
c
(
A
∩
B
)
=
c
+
d
+
e
所以
a+b+2c+d+e+2f≥a+b+2c+d+e
a
+
b
+
2
c
+
d
+
e
+
2
f
≥
a
+
b
+
2
c
+
d
+
e
⇒c(A)+c(B)≥c(A∪B)+c(A∩B)
⇒
c
(
A
)
+
c
(
B
)
≥
c
(
A
∪
B
)
+
c
(
A
∩
B
)
- posi-modularity
c(A∖B)=a+d+f c ( A ∖ B ) = a + d + f
c(B∖A)=b+e+f c ( B ∖ A ) = b + e + f
所以 a+b+2c+d+e+2f≥a+b+d+e+2f a + b + 2 c + d + e + 2 f ≥ a + b + d + e + 2 f
⇒c(A)+c(B)≥c(A∖B)+c(B∖A) ⇒ c ( A ) + c ( B ) ≥ c ( A ∖ B ) + c ( B ∖ A )
证明
假设s-t割中,W是s所在的割集 ,
u,v∈W
u
,
v
∈
W
, X是u-v割中u所在的割集.我们要证明存在一种割的方法, 使得
X∈W
X
∈
W
.
1.t在X外
由于
c(X)+c(W)≥c(X∪W)+c(X∩W)
c
(
X
)
+
c
(
W
)
≥
c
(
X
∪
W
)
+
c
(
X
∩
W
)
c(X∪W)
c
(
X
∪
W
)
是s-t的一个割, 所以
c(X∪W)≥c(W)
c
(
X
∪
W
)
≥
c
(
W
)
c(X∩W)
c
(
X
∩
W
)
是u-v的一个割, 所以
c(X∩W)≥c(X)
c
(
X
∩
W
)
≥
c
(
X
)
所以
c(X∩W)=c(X)
c
(
X
∩
W
)
=
c
(
X
)
2.t在X内
由于
c(X)+c(W)≥c(X∖W)+c(W∖X)
c
(
X
)
+
c
(
W
)
≥
c
(
X
∖
W
)
+
c
(
W
∖
X
)
c(X∖W)
c
(
X
∖
W
)
是s-t的一个割, 所以
c(X∖W)≥c(W)
c
(
X
∖
W
)
≥
c
(
W
)
c(W∖X)
c
(
W
∖
X
)
是u-v的一个割, 所以
c(W∖X)≥c(X)
c
(
W
∖
X
)
≥
c
(
X
)
所以
c(W∖X)=c(X)
c
(
W
∖
X
)
=
c
(
X
)
综上,总有一种方法使得
X∈W
X
∈
W
.
对于有向图也有类似证明.
这是Gomory-Hu Tree的理论基础,同时也说明大小不同的最小割的数量是n-1.
Consult
https://files.cnblogs.com/files/the-unbeatable/Lecture6.pdf
Problem
BZOJ 2229 [ZJOI2011] 最小割
Description
问无向图中所有无序点对中,最小割小于X的对数有多少对.
Code
// BZOJ 2229 ZJOI2011 最小割
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#include <vector>
using namespace std;
typedef unsigned int uint;
uint INF = - 1;
namespace NetWork {
static const int V = 155, E = 2e4;
struct edge {
int nxt, to;
uint cap;
} e[E];
int fir[V], dis[V], lst[V], vs[V];
static int cnt = 1;
inline void addedge(int x, int y, int c) {
e[++ cnt] = (edge) { fir[x], y, (uint)c }; fir[x] = cnt;
e[++ cnt] = (edge) { fir[y], x, 0 }; fir[y] = cnt;
}
inline int Bfs(int S, int T) {
memset(dis, -1, sizeof dis);
dis[S] = 0;
queue <int> Q;
Q.push(S);
int x;
while (!Q.empty()) {
x = Q.front(); Q.pop();
for (int i = fir[x]; i; i = e[i].nxt)
if (e[i].cap && dis[e[i].to] == -1) {
Q.push(e[i].to);
dis[e[i].to] = dis[x] + 1;
}
}
if (dis[T] != -1) memcpy(lst, fir, sizeof fir);
return dis[T];
}
inline uint Dfs(int x, int T, uint flow) {
if (x == T) return flow;
vs[x] = 1;
uint res = 0, t;
for (int &i = lst[x]; i; i = e[i].nxt)
if (e[i].cap && !vs[e[i].to] && dis[e[i].to] > dis[x]) {
t = Dfs(e[i].to, T, min(flow, e[i].cap));
flow -= t; res += t;
e[i].cap -= t; e[i ^ 1].cap += t;
if (!flow) break;
}
vs[x] = 0;
return res;
}
inline uint Dinic(int S, int T) {
uint r = 0;
for (int i = 2; i <= cnt; e[i ^ 1].cap = 0, i += 2)
e[i].cap += e[i ^ 1].cap;
while (Bfs(S, T) != -1) r += Dfs(S, T, INF);
return r;
}
inline void clear() { memset(fir, 0, sizeof fir); cnt = 1; }
}
using namespace NetWork;
uint G[V][V];
int id[V], tmp[V];
int vis[V], cur = 0;
vector <pair <int, uint> > Edge[V];
inline void dfs(int x) {
vis[x] = cur;
for (int i = fir[x]; i; i = e[i].nxt)
if (e[i].cap && vis[e[i].to] != cur) dfs(e[i].to);
}
inline void Build(int l, int r) {
if (l >= r) return;
int S = id[l], T = id[l + 1];
uint V = Dinic(S, T);
cur ++; dfs(S);
int p = l, q = r;
for (int i = l; i <= r; i ++) {
if (vis[id[i]] == cur) tmp[p ++] = id[i];
else tmp[q --] = id[i];
}
Edge[S].push_back(make_pair(T, V));
Edge[T].push_back(make_pair(S, V));
for (int i = l; i <= r; i ++) id[i] = tmp[i];
Build(l, p - 1);
Build(q + 1, r);
}
inline void travel(int x, int fa, int src, uint dis) {
G[src][x] = dis;
for (vector <pair <int, uint> > :: iterator it = Edge[x].begin(); it != Edge[x].end(); it ++)
if (it -> first != fa)
travel(it -> first, x, src, min(dis, it -> second));
}
int main() {
int T, n, m, q, a, x, y, c;
scanf("%d", &T);
while (T --) {
clear();
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++) id[i] = i, Edge[i].clear();
for (int i = 1; i <= m; i ++) {
scanf("%d%d%d", &x, &y, &c);
addedge(x, y, c); addedge(y, x, c);
}
memset(G, -1, sizeof G);
Build(1, n);
for (int i = 1; i <= n; i ++) travel(i, 0, i, INF);
scanf("%d", &q);
for (; q; q --) {
scanf("%d", &x);
a = 0;
for (int i = 1; i <= n; i ++)
for (int j = 1; j < i; j ++)
if (G[i][j] <= (uint)x) a ++;
printf("%d\n", a);
}
puts("");
}
return 0;
}