【bzoj1797】[Ahoi2009]Mincut 最小割 最小割的可行边与必须边

本文介绍了Ahoi2009比赛中的一道最小割问题,涉及如何判断最小割中的可行边和必须边。通过构建残余网络,使用Tarjan算法找出强连通分量,并分析了满流边在最小割中的角色。同时提供了数据规模和样例解析,以及解题思路。
摘要由CSDN通过智能技术生成

Description

A,B两个国家正在交战,其中A国的物资运输网中有N个中转站,M条单向道路。设其中第i (1≤i≤M)条道路连接了vi,ui两个中转站,那么中转站vi可以通过该道路到达ui中转站,如果切断这条道路,需要代价ci。现在B国想找出一个路径切断方案,使中转站s不能到达中转站t,并且切断路径的代价之和最小。 小可可一眼就看出,这是一个求最小割的问题。但爱思考的小可可并不局限于此。现在他对每条单向道路提出两个问题: 问题一:是否存在一个最小代价路径切断方案,其中该道路被切断? 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断? 现在请你回答这两个问题。

Input

第一行有4个正整数,依次为N,M,s和t。第2行到第(M+1)行每行3个正 整数v,u,c表示v中转站到u中转站之间有单向道路相连,单向道路的起点是v, 终点是u,切断它的代价是c(1≤c≤100000)。 注意:两个中转站之间可能有多条道路直接相连。 同一行相邻两数之间可能有一个或多个空格。

Output

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

Sample Input

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

Sample Output

1 0

1 0

0 0

1 0

0 0

1 0

1 0

HINT

设第(i+1)行输入的边为i号边,那么{1,2},{6,7},{2,4,6}是仅有的三个最小代价切割方案。它们的并是{1,2,4,6,7},交是 。 【数据规模和约定】 测试数据规模如下表所示 数据编号 N M 数据编号 N M 1 10 50 6 1000 20000 2 20 200 7 1000 40000 3 200 2000 8 2000 50000 4 200 2000 9 3000 60000 5 1000 20000 10 4000 60000

2015.4.16新加数据一组,可能会卡掉从前可以过的程序。

Source

Day1


一句话题意:求图中最小割的可行边、必须边。

%hzwer %jcvb

在残余网络上跑tarjan求出所有SCC,记id[u]为点u所在SCC的编号。显然有id[s]!=id[t](否则s到t有通路,能继续增广)。

①对于任意一条满流边(u,v),(u,v)能够出现在某个最小割集中,当且仅当id[u]!=id[v];
②对于任意一条满流边(u,v),(u,v)必定出现在最小割集中,当且仅当id[u]==id[s]且id[v]==id[t]。

<==将每个SCC缩成一个点,得到的新图就只含有满流边了。那么新图的任一s-t割都对应原图的某个最小割,从中任取一个把id[u]和id[v]割开的割即可证明。


<==:假设将(u,v)的边权增大,那么残余网络中会出现s->u->v->t的通路,从而能继续增广,于是最大流流量(也就是最小割容量)会增大。这即说明(u,v)是最小割集中必须出现的边。

题解转自hzwer

感觉说的很有道理…

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;

const int INF = 1000000010;
const int SZ = 1000010;

int head[SZ],nxt[SZ],tot = 1,s,e;

struct edge{
    int f,t,d;
}l[SZ];

void build(int f,int t,int d)
{
    l[++ tot].t = t;
    l[tot].d = d;
    l[tot].f = f;
    nxt[tot] = head[f];
    head[f] = tot;
}

void insert(int f,int t,int d)
{
    build(f,t,d); build(t,f,0);
}

queue<int> q;
int deep[SZ];

bool bfs()
{
    memset(deep,0,sizeof(deep));
    deep[s] = 1;
    while(q.size()) q.pop();
    q.push(s);
    while(q.size())
    {
        int u = q.front(); q.pop();
        for(int i = head[u];i;i = nxt[i])
        {
            int v = l[i].t;
            if(!deep[v] && l[i].d)
            {
                deep[v] = deep[u] + 1;
                q.push(v);
                if(v == e) return true;
            }
        }
    }
    return false;
}

int dfs(int u,int flow)
{
    if(u == e || flow == 0) return flow;
    int rest = flow;
    for(int i = head[u];i;i = nxt[i])
    {
        int v = l[i].t;
        if(deep[v] == deep[u] + 1 && l[i].d)
        {
            int f = dfs(v,min(rest,l[i].d));
            if(f > 0)
            {
                rest -= f;
                l[i].d -= f;
                l[i ^ 1].d += f;
                if(rest == 0) break;
            }
            else deep[v] = 0;
        }
    }
    if(flow - rest == 0) deep[u] = 0;
    return flow - rest;
}


int dinic()
{
    int ans = 0;
    while(bfs())
    {
        int tmp = dfs(s,INF);
        if(!tmp) break;
        ans += tmp;
    }
    return ans;
}

int low[SZ],dfn[SZ],dfs_clock = 0;
int scccnt = 0,sccnum[SZ];
stack<int> S;
void dfs_scc(int u)
{
    low[u] = dfn[u] = ++ dfs_clock;
    S.push(u);
    for(int i = head[u];i;i = nxt[i])
    if(l[i].d)
    {
        int v = l[i].t;
        if(!dfn[v])
        {
            dfs_scc(v);
            low[u] = min(low[u],low[v]);
        }
        else if(!sccnum[v])
        {
            low[u] = min(low[u],dfn[v]);
        }
    }
    if(low[u] == dfn[u])
    {
        scccnt ++;
        while(233)
        {
            int x = S.top(); S.pop();
            sccnum[x] = scccnt;
            if(x == u) break;
        }
    }
}


int ff[SZ],tt[SZ],dd[SZ];

int main()
{
    int n,m;
    scanf("%d%d%d%d",&n,&m,&s,&e);
    for(int i = 1;i <= m;i ++)
    {
        scanf("%d%d%d",&ff[i],&tt[i],&dd[i]);
        insert(ff[i],tt[i],dd[i]);
    }
    dinic();
    for(int i = 1;i <= n;i ++)
        if(!dfn[i])
            dfs_scc(i);
//  for(int i = 1;i <= n;i ++)
//      printf("%d ",sccnum[i]); puts("");
    for(int i = 2;i <= tot;i += 2)
    {
        if(!l[i].d)
        {
            if(sccnum[l[i].f] != sccnum[l[i].t])
                printf("1 ");
            else
                printf("0 ");

            if(sccnum[l[i].f] == sccnum[s] && sccnum[l[i].t] == sccnum[e])
                printf("1\n");
            else
                printf("0\n");
        }
        else
            puts("0 0");
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值