[BZOJ]1797: [Ahoi2009]Mincut 最小割 网络流+强连通

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

题解:

显然,只有满流边有可能被割。对于原图先跑一遍网络流,之后再对残量网络跑一遍强连通(此时的边包括反向弧)。对于每一条满流边,第一个问当belong[u]!=belong[v]时,输出1。(belong[i]为i所属的连通分量)因为u->v的这条边是满流的,它的反向边v->u显然不是满流,若u,v属于同一个连通分量,则u->v还存在路径,割掉此边无意义。第二个问,当belong[st]==belong[u]且belong[v]==belong[ed]时输出1。显然,缩点之后的图的最小割就是原图的最小割(割联通分量中的边无意义),所以满足以上条件,此边一定在割集中。

代码:

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=8000;
int h[maxn];
struct Edge{int x,y,d,next;}e[160010];
int N,M,st,ed;
int last[maxn],len=1;
void ins(int x,int y,int d)
{
    int t=++len;
    e[t].x=x;e[t].y=y;e[t].d=d;
    e[t].next=last[x];last[x]=t;
}
void addedge(int x,int y,int d){ins(x,y,d);ins(y,x,0);}
queue<int>q;
bool bfs()
{
    memset(h,0,sizeof(h));h[st]=1;
    q.push(st);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=last[x];i;i=e[i].next)
        if(h[e[i].y]==0&&e[i].d)q.push(e[i].y),h[e[i].y]=h[x]+1;
    }
    return h[ed];
}
int dfs1(int x,int f)
{
    if(x==ed)return f;
    int s=0,t;
    for(int i=last[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(h[y]==h[x]+1&&e[i].d&&s<f)
        {
            t=dfs1(y,min(f-s,e[i].d));
            s+=t;e[i^1].d+=t;e[i].d-=t;
        }
    }
    if(s==0)h[x]=0;
    return s;
}
int low[maxn],dfn[maxn],id=0,sta[maxn],top=0,belong[maxn],cnt=0;
bool in[maxn];
void dfs2(int x)
{
    sta[++top]=x;in[x]=true;
    low[x]=dfn[x]=++id;
    for(int i=last[x];i;i=e[i].next)
    if(e[i].d)
    {
        int y=e[i].y;
        if(dfn[y]==-1)dfs2(y),low[x]=min(low[x],low[y]);
        else if(in[y])low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x])
    {
        cnt++;
        int i;
        do
        {
            i=sta[top--];
            belong[i]=cnt;
            in[i]=false;
        }while(i!=x);
    }
}
int main()
{
    memset(dfn,-1,sizeof(dfn));
    scanf("%d%d%d%d",&N,&M,&st,&ed);
    for(int i=1;i<=M;i++)
    {
        int v,u,c;
        scanf("%d%d%d",&v,&u,&c); 
        addedge(v,u,c);
    }
    int ans=0;
    while(bfs())ans+=dfs1(st,1<<30);
    for(int i=1;i<=N;i++)
    if(dfn[i]==-1)dfs2(i);
    for(int i=2;i<=len;i+=2)
    if(e[i].d)puts("0 0");
    else
    {
        if(belong[e[i].x]!=belong[e[i].y])printf("1 ");
        else printf("0 ");
        if(belong[e[i].x]==belong[st]&&belong[e[i].y]==belong[ed])puts("1");
        else puts("0");
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值