BZOJ2229: [Zjoi2011]最小割(最小割樹)
最小割樹
演算法
初始時把所有點放在一個集合
從中任選兩個點出來跑原圖中的最小割
然後按照
集合與
集合的歸屬把當前集合劃分成兩個集合,遞迴處理
這樣一共跑了
次最小割
可以證明圖中任意一對點之間的最小割的數值都包含在這
把每次求出的最小割看成是兩個點之間的邊,可以建出一棵樹
定理1
任意三點之間的最小割一定是兩個相等的較小值和一個較大值
證明
設任意三點
設
是這三者中的最小值
那麼在做
割時,
一定屬於其中的某一個集合,設其與
同屬於一個集合
那麼就有
又因為
,所以兩者相等
定理2
圖中至多隻有 種不同的最小割
證明
編了一個構造的證明
首先根據定理
,可以轉化成如下問題:
個點的無向完全圖,要給每一條邊染色,要求每個三元環上有兩條邊同色,求最多可以染多少種顏色
考慮歸納法
假設現在已經有一條長度大於
的鏈
到
上的邊的顏色互不相同
考慮染
這條邊
如果鏈長
,那麼
只能選擇鏈上某條邊的顏色
如果鏈長
,假設兩個端點在鏈上的不是
的邊的顏色都是鏈上某條邊的顏色
那麼取出其中兩條
和
,
只能選擇兩個中其中一條邊的顏色,即鏈上某條邊的顏色
所以可以得到,顏色互不相同的邊不能形成環,所以最優的顯然是樹,即 中顏色
Sol
至此問題已經完美解決
建出最小割樹,任意兩個點的最小割就是路徑邊權最小值
直接暴力也可以
複雜度貌似
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn(505);
const int inf(1e9);
int first[maxn], n, m, cnt, lev[maxn], vis[maxn], s, t, id[maxn], tmp[maxn], cur[maxn], mincut[maxn][maxn], record[maxn << 4];
queue <int> q;
struct Edge {
int to, next, w;
} edge[maxn << 4];
inline void Add(int u, int v, int w) {
edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
record[cnt - 1] = record[cnt - 2] = w;
}
inline int Bfs() {
memset(lev, 0, sizeof(lev)), lev[s] = 1, q.push(s);
register int u, e, v;
while (!q.empty()) {
for (u = q.front(), q.pop(), e = first[u]; ~e; e = edge[e].next)
if (edge[e].w && !lev[v = edge[e].to]) lev[v] = lev[u] + 1, q.push(v);
}
return lev[t];
}
int Dfs(int u, int maxf) {
if (u == t) return maxf;
register int ret = 0, &e = cur[u], f, v;
for (; ~e; e = edge[e].next)
if (edge[e].w && lev[v = edge[e].to] == lev[u] + 1){
f = Dfs(v, min(edge[e].w, maxf - ret));
ret += f, edge[e].w -= f, edge[e ^ 1].w += f;
if (ret == maxf) return ret;
}
if (!ret) lev[u] = 0;
return ret;
}
inline int Dinic() {
register int ret = 0, i;
for (i = 0; i < cnt; ++i) edge[i].w = record[i];
while (Bfs()) memcpy(cur, first, sizeof(cur)), ret += Dfs(s, inf);
return ret;
}
void Mark(int u) {
if (vis[u]) return;
register int e;
for (vis[u] = 1, e = first[u]; ~e; e = edge[e].next) if (edge[e].w) Mark(edge[e].to);
}
inline void Solve(int l, int r) {
if (l >= r) return;
memset(vis, 0, sizeof(vis)), s = id[l], t = id[r];
register int i, j, mid, d = Dinic();
for (Mark(s), j = 0, i = l; i <= r; ++i) if (vis[id[i]]) tmp[++j] = id[i];
for (mid = l + j - 1, i = l; i <= r; ++i) if (!vis[id[i]]) tmp[++j] = id[i];
for (i = l; i <= r; ++i) id[i] = tmp[i - l + 1];
for (i = 1; i <= n; ++i)
if (vis[i])
for (j = 1; j <= n; ++j)
if (i != j && !vis[j]) mincut[i][j] = mincut[j][i] = min(mincut[i][j], d);
Solve(l, mid), Solve(mid + 1, r);
}
int main() {
register int i, j, u, v, w, test;
scanf("%d", &test);
while (test--) {
memset(first, -1, sizeof(first)), cnt = 0;
scanf("%d%d", &n, &m);
for (i = 1; i <= m; ++i) scanf("%d%d%d", &u, &v, &w), Add(u, v, w);
for (i = 1; i <= n; ++i) id[i] = i;
memset(mincut, 63, sizeof(mincut)), Solve(1, n), scanf("%d", &w);
while (w--) {
scanf("%d", &u), v = 0;
for (i = 1; i <= n; ++i)
for (j = i + 1; j <= n; ++j) v += mincut[i][j] <= u;
printf("%d\n", v);
}
putchar('\n');
}
return 0;
}
參考文獻
- 某鴿姓選手的網路流課件
2. 垃圾博主自己 yy