【AHOI2009】最小割

题目

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

Data Constraint

Hint

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

题解

这一题问的其实就是对于图中的某一条边它是否可能是某种最小割方案的一条割边以及它是不是必须要割弃的边

不妨先把最大流的流的图画出来,发现如果只看这一幅图无论怎么割答案都是一样的,也就是最小割,但是因为除了这一些边之外还有其他没有流过的边,所以有多种情况

给出几个结论
1:一条边可能成为最小割中的一条割边仅当其是一条满流的边,不然就亏了,可以看看上面的那一句话,如果切这样的边最后的总代价肯定是大于最小割的

2:考虑什么时候一条边一定是最小割中的一条边,发现如果在参与网络中s与u相连(s可以到达u)切v与t相连那么它一定是一条一定要割去的边,因为如果这一条边的流量发生改变的话那么最大流就会改变,也就是最小割也会改变,换言之就是这一条边一定要割去

3:考虑什么时候一条边可能被割去,如果在残余网络中u依然可以到v那么我们如果割去u–>v那么最大流并不会减少相当于其流量大小的值,也就是说这一条边不可能成为割边

具体实现的话用tarjan就好了,因为残余网络里面有反向边

贴代码

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;

const int maxn=4005,maxm=60005;

int fi[maxn],ne[maxm*2],dui[maxm*2],dui1[maxm*2],qc[maxn],go[maxn];
int dfn[maxn],cc[maxn],low[maxn],h[maxn],de[maxn],st[maxn];
int a[maxm][4];
bool bb[maxn],bz[maxn];
int i,j,k,l,m,n,x,y,z,s,t,now,QAQ,cs,ans;

void add(int x,int y,int z){
    if (fi[x]==0) fi[x]=++now; else ne[qc[x]]=++now;
    dui[now]=y; dui1[now]=z; qc[x]=now;
}
bool bfs(){
    memset(de,255,sizeof(de));
    de[s]=1;
    h[1]=s;
    int i=1,j=0;
    while (i>j){
        j++; x=h[j];
        k=fi[x];
        while (k){
            if (de[dui[k]]>0 || dui1[k]<=0){
                k=ne[k]; continue;
            }
            de[dui[k]]=de[x]+1;
            h[++i]=dui[k];
            k=ne[k];
        }
    }
    if (de[t]>0) return true;
    return false;
}
int dinic(int x,int w){
    if (x==t) return w;
    int i=go[x],p,used=0;
    while (i){
        if (de[dui[i]]==de[x]+1 && dui1[i]>0){
            p=dinic(dui[i],min(w-used,dui1[i]));
            go[x]=i;
            if (p){
                dui1[i]-=p;
                dui1[i xor 1]+=p;
                used+=p;
                //return p;
            }
//          if (used==w) return used;
        }
        i=ne[i];
    }
    return used;
}
void tarjan(int x){
    dfn[x]=low[x]=++QAQ; bz[x]=bb[x]=true;
    st[++now]=x;
    int i=fi[x];
    while (i){
        if (bz[dui[i]]==false && dui1[i]>0){
            tarjan(dui[i]);
            low[x]=min(low[x],low[dui[i]]);
        } else if (bb[dui[i]]==true && dui1[i]) low[x]=min(low[x],low[dui[i]]);
        i=ne[i];
    }
    if (dfn[x]==low[x]){
        cs++;
        while (true){
            bb[st[now]]=false;
            cc[st[now]]=cs;
            now--;
            if (st[now+1]==x) break;
        }
    }
}
int main(){
//  freopen("1665.in","r",stdin);
    scanf("%d%d%d%d",&n,&m,&s,&t); now=1;
    fo(i,1,m){
        scanf("%d%d%d",&x,&y,&z); a[i][1]=x; a[i][2]=y;
        add(x,y,z); add(y,x,0);
    }
    while (bfs()){
        fo(i,1,n) go[i]=fi[i];
        ans+=dinic(s,666666);
    }
    fo(i,1,n) if (bz[i]==false){
        now=0;
        tarjan(i);
    }
    fo(i,1,m){
        z=i*2;
        if (dui1[z]!=0) printf("0 0\n"); else{
            if (cc[a[i][1]]==cc[a[i][2]]) printf("0 "); else printf("1 ");
            if (cc[a[i][1]]==cc[s] && cc[a[i][2]]==cc[t]) 
                printf("1\n"); else printf("0\n");
        }
    }
    return 0;
}

你可能感兴趣的:(网络流)