【 bzoj 1797 】[Ahoi2009]Mincut 最小割 - 经典题

  这个题就是要求一条边在最小割里面的充分条件和必要条件。
  首先考虑第一问,也即求充分条件。
  我们先对这个图进行最大流算法得到任意一个最小割。设这个时候的残量网络为 Gf 。那么 Gf 中的点集可以显然地被分成了三部分,一部分是源 s 可以到达的,一部分是可以到达t的,剩下的独立成一部分。
  可以证明,第一类点一定会被归为最小割中的 S 集,第二类点一定会被归为T集,第三类点两个集合均有可能。显然割边联接着的一定是 S 集点和T集点且在 Gf 中一定满流。
  考虑一条满流边 (u,v) 。如果 Gf 中存在 uv 的路径,这条边一定不会在割集里面,反之亦然,证明是显然的。
  这样我们就可以对残量网络缩点,对每条满流边判断是否在同一个强联通分量内即可求出第一问。
  对于第二问,因为割边连着 S 集和T集,所以只需判断一条边是否连着 S 连通块和T连通块即可。
  代码:
  

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i = a , _ = (b) ; i <= _ ; i ++)
#define per(i,a,b) for (int i = a , _ = (b) ; i >= _ ; i --)
#define fore(i,u)  for (int i = head[u] ; i ; i = nxt[i])

inline int rd() {
    char c = getchar();
    while (!isdigit(c)) c = getchar() ; int x = c - '0';
    while (isdigit(c = getchar())) x = x * 10 + c - '0';
    return x;
}

inline void upmin(int&a , int b) { if (a > b) a = b ; }

const int maxn = 5007;
const int maxm = 120007;
const int inf = 0x7fffffff;

typedef int arr[maxn];
typedef int adj[maxm];

arr head , belong , dis , pos , low , cur;
adj fr , to , nxt , cap , flow;

int n , m , S , T , ett , scc_cnt , dfs_clock;

stack<int> s;
queue<int> Q;

inline void ins(int u , int v , int c) {
    fr[++ ett] = u , to[ett] = v , nxt[ett] = head[u] , cap[ett] = c , head[u] = ett;
    fr[++ ett] = v , to[ett] = u , nxt[ett] = head[v] , cap[ett] = 0 , head[v] = ett;
}

void input() {
    ett = 1;
    n = rd() , m = rd() , S = rd() , T = rd();
    rep (i , 1 , m) {
        int u = rd() , v = rd() , w = rd();
        ins(u , v , w);
    }
}

bool bfs() {
    rep (i , 1 , n) dis[i] = inf;
    dis[S] = 0 , Q.push(S);
    while (!Q.empty()) {
        int u = Q.front() ; Q.pop();
        fore (i , u) {
            int v = to[i];
            if (dis[v] == inf && cap[i] > flow[i])
                dis[v] = dis[u] + 1 , Q.push(v);
        }
    }
    return dis[T] != inf;
}

int dfs(int u , int a) {
    if (u == T || !a) return a;
    int ret = 0 , f;
    for (int&i = cur[u] ; i ; i = nxt[i]) {
        int v = to[i];
        if (dis[v] == dis[u] + 1 && (f = dfs(v , min(a , cap[i] - flow[i]))) > 0) {
            flow[i] += f , flow[i ^ 1] -= f , ret += f , a -= f;
            if (!a) break;
        }
    }
    return ret;
}

void tarjan(int u , int p) {
    pos[u] = low[u] = ++ dfs_clock;
    s.push(u);
    fore (i , u) if (cap[i] > flow[i]) {
        int v = to[i];
//      printf("%d %d\n" , u , v);
        if (!pos[v]) {
            tarjan(v , u);
            upmin(low[u] , low[v]);
        } else if (!belong[v])
            upmin(low[u] , pos[v]);
    }
    if (low[u] == pos[u]) {
        scc_cnt ++;
        for (;;) {
            int x = s.top() ; s.pop();
            belong[x] = scc_cnt;
            if (x == u) break;
        }
    }
}

void solve() {
    int mx_flow = 0;
    while (bfs()) {
        rep (i , 1 , n) cur[i] = head[i];
        mx_flow += dfs(S , inf);
    }
    rep (u , 1 , n) if (!pos[u]) tarjan(u , 0);
    rep (i , 1 , m) {
        int u = fr[i << 1] , v = to[i << 1] , f = (flow[i << 1] == cap[i << 1]);
        printf("%d%c" , belong[u] != belong[v] && f , ' ');
        printf("%d%c" , belong[u] == belong[S] && belong[v] == belong[T] , '\n');
    }
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("data.txt" , "r" , stdin);
    #endif
    input();
    solve();
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值