BZOJ1797: [Ahoi2009]Mincut 最小割(洛谷P4126)

版权声明:蒟蒻Blog随意转载 https://blog.csdn.net/a1799342217/article/details/82857645

最小割 Tarjan

BZOJ题目传送门
洛谷题目传送门

结论题。。。

跑一遍最小割,对残量网络进行缩点,使得剩下的边都是满流边。
一条边属于最小割的并集当这条边两个端点所在的联通块不同。
一条边属于最小割的交集当这条边两个端点所在的联通块分别与源点和汇点相同。

下面给出口胡:

如果这条边的两个端点在同一个联通块内,那么它就不是满流边,一定没有割它流入的满流边划算,也就不是最小割。
一条边如果一个点属于源点所在的联通块,它可以和一个点属于汇点所在的联通块这样的边互相代替。

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

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

​ —hzwer

最后统计答案的时候后向弧不能算。

代码:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 4005
#define M 60005
#define F inline
using namespace std;
struct edge{ int nxt,to,v,x,f; }ed[M<<1];
int n,m,k=1,ti,s,t,tp,p,h[N],tmp[N],f[N],d[N],q[N];
int stk[N],dfn[N],low[N],num[N];
F char readc(){
	static char buf[100000],*l=buf,*r=buf;
	if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
	return l==r?EOF:*l++;
}
F int _read(){
	int x=0; char ch=readc();
	while (!isdigit(ch)) ch=readc();
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
	return x;
}
F void add(int x,int y,int z){
	ed[++k]=(edge){h[x],y,z,x},h[x]=k;
	ed[++k]=(edge){h[y],x,0,y},h[y]=k;
}
F bool bfs(){
	int l=0,r=1; q[1]=s,d[s]=0,f[s]=++ti;
	while (l<r){
		int x=q[++l];
		for (int i=h[x],v;i;i=ed[i].nxt)
			if (ed[i].v>ed[i].f&&f[v=ed[i].to]!=ti)
				d[v]=d[x]+1,q[++r]=v,f[v]=ti;
	}
	return f[t]==ti;
}
int dfs(int x,int rem){
	if (x==t||!rem) return rem; int sum=0;
	for (int &i=tmp[x];i;i=ed[i].nxt)
		if (d[ed[i].to]==d[x]+1){
			int p=dfs(ed[i].to,min(rem,ed[i].v-ed[i].f));
			if (p) sum+=p,ed[i].f+=p,ed[i^1].f-=p,rem-=p;
			if (!rem) break;
		}
	return sum;
}
void Tarjan(int x){
	dfn[x]=low[x]=++p,stk[++tp]=x,f[x]=1;
	for (int i=h[x],v;i;i=ed[i].nxt)
		if (ed[i].v>ed[i].f)
			if (!dfn[v=ed[i].to]) Tarjan(v),low[x]=min(low[x],low[v]);
			else if (f[v]) low[x]=min(low[x],dfn[v]);
	if (dfn[x]==low[x]){
		while (stk[tp]!=x) num[stk[tp]]=x,f[stk[tp--]]=0;
		num[x]=x,f[x]=0,tp--;
	}
}
int main(){
	n=_read(),m=_read(),s=_read(),t=_read();
	for (int i=1,x,y,z;i<=m;i++)
		x=_read(),y=_read(),z=_read(),add(x,y,z);
	while (bfs()){ for (int i=1;i<=n;i++) tmp[i]=h[i]; dfs(s,1e9); }
	for (int i=1;i<=n;i++) f[i]=0;
	for (int i=1;i<=n;i++) if (!dfn[i]) Tarjan(i);
	for (int i=1,x,v;i<=m;i++){
		if (ed[i<<1].f<ed[i<<1].v){ puts("0 0"); continue; }
		putchar(num[v=ed[i<<1].to]!=num[x=ed[i<<1].x]?'1':'0');
		puts(num[x]==num[s]&&num[v]==num[t]?" 1":" 0");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/a1799342217/article/details/82857645