洛谷 P4126 [AHOI2009]最小割

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

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

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

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

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

最小割的可行边和必须边
然而做这道题的时候并不会这东西QAQ
首先我们考虑\((u,v)\)是可行边需要满足的条件

  • 满流
  • 在残余网络u和v不连通

满流就不用说了,如果u和v连通的话那么这个图也是连通的,就不是最小割了
然后考虑\((u,v)\)是必须边需要满足的条件

  • 满流
  • 在残余网络中u和起点s连通,v和终点t连通

我们思考第二个条件,如果u和s不连通或者v和t连通,那么在单独的路径上一定会存在一条可行边,这个割掉和\((u,v)\)割掉是等效的,就不是必须边了
所以我们可以直接对残余网络做tarjan,可行边需要满足u和v不在同一个强连通分量,必须边满足u和s在同一个强连通分量,v和t在同一个强连通分量
Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 4000;
const int M = 6e4;
const int inf = 1e9;
using namespace std;
int id[M + 5],co_cnt,co[M + 5],n,m,s,t,dep[M + 5],cur[M + 5],head[M + 5],nxt[M * 2 + 5],q[M + 5],ans,edge_cnt = 1,U[M + 5],V[M + 5],dfn[M + 5],dfn_cnt,low[M + 5],stk[M + 5],top;
struct edges
{
    int to,cost;
}edge[M * 2 + 5];
int dfs(int u,int flow)
{
    if (u == t) 
        return flow;
    int sum = 0;
    for (int &i = cur[u];i;i = nxt[i])
    {
        int v = edge[i].to,w = edge[i].cost;
        if (dep[v] == dep[u] + 1 && w)
        {
            int res = dfs(v,min(flow,w));
            edge[i].cost -= res;
            edge[i ^ 1].cost += res;
            sum += res;
            flow -= res;
            if (!flow)
                return sum;
        }
    }
    if (!sum)
        dep[u] = 0;
    return sum;
}
int bfs()
{
    for (int i = 1;i <= n;i++)
        dep[i] = 0,cur[i] = head[i];
    dep[s] = 1;
    int l = 1,r = 0;
    q[++r] = s;
    while (l <= r)
    {
        int u = q[l++];
        if (u == t) 
            return 1;
        for (int i = head[u];i;i = nxt[i])
        {
            int v = edge[i].to,w = edge[i].cost;
            if (!dep[v] && w)
            {
                dep[v] = dep[u] + 1;
                q[++r] = v;
            }
        }
    }
    return 0;
}
void add_edge(int u,int v,int w)
{
    edge[++edge_cnt] = (edges){v,w};
    nxt[edge_cnt] = head[u];
    head[u] = edge_cnt;
}
void tarjan(int u)
{
    dfn[u] = low[u] = ++dfn_cnt;
    stk[++top] = u;
    for (int i = head[u];i;i = nxt[i])
    {
        int v = edge[i].to,w = edge[i].cost;
        if (!w)
            continue;
        if (!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else
            if (!co[v])
                low[u] = min(low[u],dfn[v]);
    }
    if (low[u] == dfn[u])
    {
        co[u] = ++co_cnt;
        while (stk[top] != u)
            co[stk[top--]] = co_cnt;
        top--;
    }
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&s,&t);
    int w;
    for (int i = 1;i <= m;i++)
    {
        scanf("%d%d%d",&U[i],&V[i],&w);
        add_edge(U[i],V[i],w);
        id[i] = edge_cnt;
        add_edge(V[i],U[i],0);
    }
    while (bfs())
        ans += dfs(s,inf);
    for (int i = 1;i <= n;i++)
        if (!dfn[i])
            tarjan(i);
    for (int i = 1;i <= m;i++)
    {
        if (!edge[id[i]].cost && co[U[i]] != co[V[i]])
            printf("1 ");
        else
            printf("0 ");
        if (!edge[id[i]].cost && co[U[i]] == co[s] && co[V[i]] == co[t])
            printf("1 ");
        else
            printf("0 ");
        putchar(10);
    }
    return 0;
}
posted @ 2020-06-15 17:55  eee_hoho  阅读(123)  评论(0编辑  收藏  举报