BZOJ2229: [Zjoi2011]最小割(最小割树)

传送门

最小割树

算法

初始时把所有点放在一个集合
从中任选两个点出来跑原图中的最小割
然后按照 s s s 集合与 t t t 集合的归属把当前集合划分成两个集合,递归处理
这样一共跑了 n − 1 n − 1 n1 次最小割
可以证明图中任意一对点之间的最小割的数值都包含在这 n − 1 n − 1 n1 个数值当中
把每次求出的最小割看成是两个点之间的边,可以建出一棵树

定理1

任意三点之间的最小割一定是两个相等的较小值和一个较大值

证明
设任意三点 a , b , c a, b, c a,b,c 之间的最小割分别为 m i n c u t ( a , b ) , m i n c u t ( a , c ) , m i n c u t ( b , c ) mincut(a, b), mincut(a, c), mincut(b, c) mincut(a,b),mincut(a,c),mincut(b,c)
m i n c u t ( a , b ) mincut(a, b) mincut(a,b) 是这三者中的最小值
那么在做 a − b a − b ab 割时, c c c 一定属于其中的某一个集合,设其与 a a a 同属于一个集合
那么就有 m i n c u t ( b , c ) ≤ m i n c u t ( a , b ) mincut(b, c) \le mincut(a, b) mincut(b,c)mincut(a,b)
又因为 m i n c u t ( a , b ) ≤ m i n c u t ( b , c ) mincut(a, b) \le mincut(b, c) mincut(a,b)mincut(b,c),所以两者相等

定理2

图中至多只有 n − 1 n − 1 n1 种不同的最小割

证明
编了一个构造的证明
首先根据定理 1 1 1 ,可以转化成如下问题:

n n n 个点的无向完全图,要给每一条边染色,要求每个三元环上有两条边同色,求最多可以染多少种颜色

考虑归纳法
假设现在已经有一条长度大于 1 1 1 的链 a a a b b b 上的边的颜色互不相同
考虑染 ( a , b ) (a,b) (a,b) 这条边
如果链长 = 2 =2 =2,那么 ( a , b ) (a,b) (a,b) 只能选择链上某条边的颜色
如果链长 > 2 >2 >2,假设两个端点在链上的不是 ( a , b ) (a,b) (a,b) 的边的颜色都是链上某条边的颜色
那么取出其中两条 ( a , c ) (a,c) (a,c) ( c , b ) (c,b) (c,b) ( a , b ) (a,b) (a,b) 只能选择两个中其中一条边的颜色,即链上某条边的颜色

所以可以得到,颜色互不相同的边不能形成环,所以最优的显然是树,即 n − 1 n-1 n1 中颜色

Sol

至此问题已经完美解决
建出最小割树,任意两个点的最小割就是路径边权最小值
直接暴力也可以
复杂度貌似 Θ ( n 3 m ) \Theta(n^3m) Θ(n3m) ? ? ? ??? ???

# include 
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;
}

参考文献

  1. 某鸽姓选手的网络流课件
    2. 垃圾博主自己 yy

你可能感兴趣的:(模板\算法\知识点总结,网络流)