最小割树:证明与例题

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(次模性/子模性)

Submodularity.png

如图: c ( A ) = a + c + e + f , c ( B ) = b + c + d + f
c ( A B ) = a + b + c , c ( A B ) = c + 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 )

  • posi-modularity
    c ( A B ) = a + d + f
    c ( B A ) = b + e + f
    所以 a + b + 2 c + d + e + 2 f a + b + d + e + 2 f
    c ( A ) + c ( B ) c ( A B ) + c ( B A )

证明

假设s-t割中,W是s所在的割集 , u , v W , X是u-v割中u所在的割集.我们要证明存在一种割的方法, 使得 X W .
Submodularity1.png

1.t在X外
由于 c ( X ) + c ( W ) c ( X W ) + c ( X W )
c ( X W ) 是s-t的一个割, 所以 c ( X W ) c ( W )
c ( X W ) 是u-v的一个割, 所以 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 W ) 是s-t的一个割, 所以 c ( X W ) c ( W )
c ( W X ) 是u-v的一个割, 所以 c ( W X ) c ( X )
所以 c ( W X ) = c ( X )
综上,总有一种方法使得 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;
}

猜你喜欢

转载自blog.csdn.net/The_Unbeatable/article/details/81537852