1797: [Ahoi2009]Mincut 最小割
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 2076 Solved: 885
[Submit][Status][Discuss]
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
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
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
分析:
我们发现最小割就是把从S到T的路径全部切断,也就是说如果去掉了最小割中的某一条边(u,v),我们无法再找到一条可以替代它的路径,因为最小割中的变一定是满流的,所以如果两个点在一个强连通分量中,我们就可以找到另一条从u到v的路径...所以只要tarjan缩点,满流边的两个点不在同一个强连通分量中就可以成为最小割的可行边...
因为如果在残留网络中可以从S到u,那么一定存在反向边可以从u到S,所以S和u一定在同一个强连通分量中...所以也可以判断SuvT是否在同一强连通分量中来找必须边...
BUT!!!!!NOTICE!!!!!
如果要使用tarjan的话说,需要注意的是不存在容量为0的边,如果存在,容量为0的边是不可能被算为必须边的,在求最小割必须边的时候是对的,但是如果是网络流的必须边就是错误的!!!!!!
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
//by NeighThorn
#define inf 0x3f3f3f3f
#define M 60000*3
using namespace std;
//大鹏一日同风起,扶摇直上九万里 const int maxn=+,maxm=*+; int n,m,S,T,cnt,id[maxm],hd[maxn],to[maxm],fl[maxm],nxt[maxm],vis[maxn],pos[maxn],flag[maxm],ans1[maxm],ans2[maxm]; int C,tim,tail,mp[maxn],dfn[maxn],low[maxn],stk[maxn],instk[maxn]; inline void add(int s,int x,int y,int l){
fl[cnt]=s;id[cnt]=l;to[cnt]=y;nxt[cnt]=hd[x];flag[cnt]=;hd[x]=cnt++;
fl[cnt]=;id[cnt]=M;to[cnt]=x;nxt[cnt]=hd[y];flag[cnt]=;hd[y]=cnt++;
} inline bool bfs(void){
memset(pos,-,sizeof(pos));
int head=,tail=,q[maxn];
q[]=S,pos[S]=;
while(head<=tail){
int top=q[head++];
for(int i=hd[top];i!=-;i=nxt[i])
if(pos[to[i]]==-&&fl[i])
pos[to[i]]=pos[top]+,q[++tail]=to[i];
}
return pos[T]!=-;
} inline int find(int v,int f){
if(v==T)
return f;
int res=,t;
for(int i=hd[v];i!=-&&f>res;i=nxt[i])
if(pos[to[i]]==pos[v]+&&fl[i])
t=find(to[i],min(f-res,fl[i])),fl[i]-=t,fl[i^]+=t,res+=t;
if(!res)
pos[v]=-;
return res;
} inline void dinic(void){
while(bfs())
while(find(S,inf));
} inline void tarjan(int root){
dfn[root]=low[root]=++tim;stk[++tail]=root;instk[root]=;
for(int i=hd[root];i!=-;i=nxt[i])
if(fl[i]){
if(dfn[to[i]]==)
tarjan(to[i]),low[root]=min(low[root],low[to[i]]);
else if(instk[to[i]])
low[root]=min(low[root],dfn[to[i]]);
}
if(low[root]==dfn[root]){
int tmp;C++;
do{
tmp=stk[tail--];instk[tmp]=;mp[tmp]=C;
}while(tmp!=root);
}
} signed main(void){
memset(hd,-,sizeof(hd));
memset(vis,,sizeof(vis));
memset(low,,sizeof(low));
memset(dfn,,sizeof(dfn));
memset(instk,,sizeof(instk));
scanf("%d%d%d%d",&n,&m,&S,&T);
for(int i=,s,x,y;i<=m;i++)
scanf("%d%d%d",&x,&y,&s),add(s,x,y,i);
dinic();C=tim=tail=;
for(int i=;i<=n;i++)
if(!dfn[i])
tarjan(i);
for(int i=;i<=n;i++)
for(int j=hd[i];j!=-;j=nxt[j])
if(flag[j]&&fl[j]==)
ans1[j]=(mp[i]==mp[to[j]]?:),ans2[j]=((mp[i]==mp[S]&&mp[to[j]]==mp[T])?:);
for(int i=;i<cnt;i+=)
printf("%d %d\n",ans1[i],ans2[i]);
return ;
}//Cap ou pas cap. Cap.
By NeighThorn