bzoj1797 [AHOI2009]最小割

题目描述

A,B两个国家正在交战,其中A国的物资运输网中有$N$个中转站,$M$条单向道路。设其中第$i (1\le i\le M)$条道路连接了$v_i,u_i$两个中转站,那么中转站$v_i$可以通过该道路到达$u_i$中转站,如果切断这条道路,需要代价$c_i$。

现在B国想找出一个路径切断方案,使中转站$s$不能到达中转站$t$,并且切断路径的代价之和最小。

小可可一眼就看出,这是一个求最小割的问题。但爱思考的小可可并不局限于此。现在他对每条单向道路提出两个问题:

  • 问题一:是否存在一个最小代价路径切断方案,其中该道路被切断?
  • 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断?

现在请你回答这两个问题。

输入格式

第一行有$4$个正整数,依次为$N,M,s,t$。

第$2$行到第$(M+1)$行每行$3$个正整数$v,u,c$,表示$v$中转站到$u$中转站之间有单向道路相连,单向道路的起点是$v$, 终点是$u$,切断它的代价是$c(1≤c≤100000)$。

注意:两个中转站之间可能有多条道路直接相连。 同一行相邻两数之间可能有一个或多个空格。

输出格式

对每条单向边,按输入顺序,依次输出一行,包含两个非$0$即$1$的整数,分别表示对问题一和问题二的回答(其中输出$1$表示是,输出$0$表示否)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。

样例输入

6 7 1 6
1 2 3
1 3 2
2 4 4
2 5 1
3 5 5
4 6 2
5 6 3

样例输出

1 0
1 0
0 0
1 0
0 0
1 0
1 0

说明

设第$(i+1)$行输入的边为$i$号边,那么${1,2},{6,7},{2,4,6}$是仅有的三个最小代价切割方案。它们的并是${1,2,4,6,7}$,交是 ${\varnothing }$ 。

$N\le 4000$,$M\le 60000$。


分析

题目大意:求最小割可行边与关键边。

可行边需要满足:在残量网络中没有$u\to v$的路径。

如果残量网络上存在$u\to v$的路径,那么在$s\to u$和$v\to t$上肯定还有其它边限制了流量,所以切$(u,v)$不是最优的。

实现方法:残量网络中肯定有$v\to u$的路径,那么我们只需要判断$u,v$是否在同一个SCC里即可。

关键边需要满足:在残量网络中有$s\to u$、$v\to t$的路径。

如果不满足,比如$s\to u$没有路径,那么割某一些$s\to u$的边并不比割$(u,v)$更劣。

实现方法:同理,判断$s,u$和$v,t$是否分别在同一个SCC里。

代码

#include <bits/stdc++.h>

const int N = 4050;
const int M = 60050;
const int INF = 0x3f3f3f3f;

struct Graph {
    struct Edge {
        int to, nxt;
        int w;
    } e[M<<1];
    int top, head[N];

    Graph(): top(0) {
        memset(head, -1, sizeof head);
    }

    void add(int u, int v, int w) {
        e[top] = (Edge){v, head[u], w};
        head[u] = top++;
    }
} G;

int n;

namespace Net {
    int s, t;
    int dis[N];

    void add(int u, int v, int w) {
        G.add(u, v, w);
        G.add(v, u, 0);
    }

    bool bfs() {
        std::queue<int> q;
        memset(dis, 0, sizeof dis);
        q.push(s);
        dis[s] = 1;
        while (!q.empty() && !dis[t]) {
            int u = q.front();
            q.pop();
            for (int i = G.head[u]; ~i; i = G.e[i].nxt) {
                int v = G.e[i].to, w = G.e[i].w;
                if (!w) continue;
                if (!dis[v]) {
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[t];
    }

    int dfs(int u, int curFlow) {
        if (u == t) return curFlow;
        for (int i = G.head[u]; ~i; i = G.e[i].nxt) {
            int v = G.e[i].to, w = G.e[i].w, flow;
            if (w && dis[v] == dis[u] + 1 && (flow = dfs(v, std::min(curFlow, w)))) {
                G.e[i].w -= flow;
                G.e[i^1].w += flow;
                return flow;
            }
        }
        dis[u] = 0;
        return 0;
    }

    int dinic(int _s, int _t) {
        s = _s; t = _t;
        int maxFlow = 0, flow;
        while (bfs()) {
            while ((flow = dfs(s, INF)))
                maxFlow += flow;
        }
        return maxFlow;
    }
}

namespace Tj {
    int dfx, dfn[N], low[N];
    std::stack<int> stk;
    int scc, bl[N];

    void dfs(int u) {
        low[u] = dfn[u] = ++dfx;
        stk.push(u);
        for (int i = G.head[u]; ~i; i = G.e[i].nxt) {
            int v = G.e[i].to, w = G.e[i].w;
            if (!w) continue;
            if (!dfn[v]) {
                dfs(v);
                low[u] = std::min(low[u], low[v]);
            } else if (!bl[v]) {
                low[u] = std::min(low[u], dfn[v]);
            }
        }
        if (low[u] == dfn[u]) {
            scc++;
            while (!stk.empty()) {
                int x = stk.top();
                stk.pop();
                bl[x] = scc;
                if (x == u) break;
            }
        }
    }

    void tarjan() {
        for (int i = 1; i <= n; i++)
            if (!dfn[i]) dfs(i);
    }
}

int main() {
    int m, s, t;
    scanf("%d%d%d%d", &n, &m, &s, &t);
    for (int i = 0, u, v, w; i < m; i++) {
        scanf("%d%d%d", &u, &v, &w);
        Net::add(u, v, w);
    }
    Net::dinic(s, t);
    Tj::tarjan();
    for (int i = 0; i < m; i++) {
        int u = G.e[i*2+1].to, v = G.e[i*2].to, w = G.e[i*2].w;
        if (!w && Tj::bl[u] != Tj::bl[v]) {
            printf("1 ");
            if (Tj::bl[s] == Tj::bl[u] && Tj::bl[v] == Tj::bl[t])
                puts("1");
            else
                puts("0");
        } else {
            puts("0 0");
        }
    }
    return 0;
}


转载请注明出处。


评论列表,共 0 条评论

    暂无评论

发表评论