P4126-[AHOI2009]最小割【网络流,tarjan】

正题

题目链接:https://www.luogu.com.cn/problem/P4126


题目大意

给出(n)个点(m)条边的一张有向图和起点终点。对于每条边求其是否是最小割的可行割/必须割

(1leq nleq 4000,1leq mleq 60000)


解题思路

一些结论吧,首先是可行割,跑一次最大流,然后如果一条边是可行割需要满足

  • 该边满流
  • 残量网络上没有(x,y)之间的环

首先满流是显然的,然后第二个结论的话,如果它们之间有环,那么从(y)顺着环的方向逆流回去的话那么最大流不变但是这条边的流量减少了。

然后必须割的话也是两个条件

  • 该边满流
  • 残量网络上(s)能到(x)(y)能到(t)

这个我直接搬之前的证明了

证明:在残量网络上(s)可以到达(x)(y)可以到达(t)那么说明若该边不割那么(s)就可以通过该边到达(t)。假设在(s)(x)的路上有同样长度的一条道路且割掉后可以使(s)到达(x)那么在残量网络上该边的流量必定为0,因为切割掉(x->y)的流量也必定会经过该边所以在残量网络上(s)就不可以到达(x)了。所以该假设不成立。
证毕

所以跑完最大流再跑一次(tarjan)然后按照上面判就好了


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;
const int N=4100,M=6e4+10,inf=1e9;
struct node{
	int to,next,w;
}a[M<<1];
int n,m,s,t,tot,cnt,num,ls[N],id[M];
int dep[N],dfn[N],low[N],col[N];
bool ins[N];
stack<int> st;queue<int> q;
void addl(int x,int y,int w){
	a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;
	a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0;
	return;
}
bool bfs(){
	while(!q.empty())q.pop();q.push(s);
	memset(dep,0,sizeof(dep));dep[s]=1;
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=ls[x];i;i=a[i].next){
			int y=a[i].to;
			if(dep[y]||!a[i].w)continue;
			dep[y]=dep[x]+1;
			if(y==t)return 1;
			q.push(y);
		}
	}
	return 0;
}
int dinic(int x,int flow){
	if(x==t)return flow;
	int rest=0,k;
	for(int i=ls[x];i;i=a[i].next){
		int y=a[i].to;
		if(dep[x]+1!=dep[y]||!a[i].w)continue;
		rest+=(k=dinic(y,min(flow-rest,a[i].w)));
		a[i].w-=k;a[i^1].w+=k;
		if(rest==flow)return rest;
	}
	if(!rest)dep[x]=0;
	return rest;
} 
void tarjan(int x){
	dfn[x]=low[x]=++cnt;
	ins[x]=1;st.push(x);
	for(int i=ls[x];i;i=a[i].next){
		int y=a[i].to;
		if(!a[i].w)continue;
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(ins[y])
			low[x]=min(low[x],dfn[y]);
	}
	if(low[x]==dfn[x]){
		int k;++num;
		do{
			k=st.top();st.pop();
			col[k]=num;ins[k]=0;
		}while(k!=x);
	}
	return;
}
signed main()
{
	scanf("%d%d%d%d",&n,&m,&s,&t);tot=1;
	for(int i=1;i<=m;i++){
		int x,y,w;id[i]=tot+1;
		scanf("%d%d%d",&x,&y,&w);
		addl(x,y,w);
	}
	while(bfs())
		dinic(s,inf);
	for(int i=1;i<=n;i++)
		if(!dfn[i])tarjan(i);
	for(int i=1;i<=m;i++){
		int x=a[id[i]^1].to,y=a[id[i]].to;
		if(a[id[i]].w==0&&col[x]!=col[y])putchar('1');else putchar('0');putchar(' ');
		if(a[id[i]].w==0&&col[x]==col[s]&&col[y]==col[t])putchar('1');else putchar('0');putchar('
');
	}
	return 0;
}
原文地址:https://www.cnblogs.com/QuantAsk/p/14626495.html