[BZOJ4519] 不同的最小割 - 分治,最小割(Gomory-Hu Tree)

题目大意:求一张图的不同的最小割个数。

这里我们用Gomory-Hu Tree的思想但却并不需要建立那样的一棵最小割树。首先我们要知道最小割不会相互跨立(ZZT不会证233),然后就可以每次随机选取两点,求出最小割后分治两边的点集即可,根据最大流最小割定理,直接跑一遍dinic就可以了。时间复杂度上界O(n^2mlog n),实际复杂度完全可以接受。

upd.如果两个最小割会相互跨立,那么显然这两个最小割要重合(否则可以通过改变其中一个割切使割更小),所以就显然了qwq

#include"bits/stdc++.h"
using namespace std;

#define gch getchar()
template<class integer>
void read(integer&x){
	x=0;int f=1;char ch=gch;
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=gch;}
	while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gch;}
	x*=f;
}

const int inf=(int)1e9;
const int N=855,M=8505;

set<int> ans;
struct Edge{
	int to,flow;
	Edge (int _=0,int __=0)
	{ to=_; flow=__;}
} e[M<<1];
int h[N],nxt[M<<1],n,m;
int S,T,d[N],q[N],l,r,cnt=1;

bool bfs(){
	memset(d,-1,sizeof(d));
	l=0,r=1;q[1]=S;d[S]=0;
	while(l<r){
		for(int i=h[q[++l]];i;i=nxt[i]){
			if(e[i].flow&&d[e[i].to]==-1){
				d[e[i].to]=d[q[l]]+1;
				q[++r]=e[i].to;
			}
		}
	}
	return d[T]!=-1;
}

int dfs(int u,int mxf){
	if(u==T)return mxf;
	int res=0,i,dr;
	for(i=h[u];i;i=nxt[i]){
		if(e[i].flow&&d[e[i].to]==d[u]+1){
			dr=dfs(e[i].to,min(mxf,e[i].flow));
			e[i].flow-=dr,e[i^1].flow+=dr;
			mxf-=dr,res+=dr;
		}
	}
	if(res==0)d[u]=-1;
	return res;
}

int dinic(){
	int cut=0;
	while(bfs())
		cut+=dfs(S,inf);
	return cut;
}

inline void reset(){
	for(int i=2;i<cnt;i+=2)
		e[i].flow=e[i^1].flow=(e[i].flow+e[i^1].flow)>>1;
}

int di[N],color[N];

void paint(int fr){
	color[fr]=1;
	for(int i=h[fr];i;i=nxt[i]){
		if(e[i].flow&&color[e[i].to]^1){
			paint(e[i].to);
		}
	}
}

void solve(int l,int r){
	if(l==r)return;
	reset();S=di[l],T=di[r];
	int mincut=dinic(),i,j;
	if(ans.find(mincut)==ans.end())
		ans.insert(mincut);
	memset(color,0,sizeof(color));
	paint(di[l]);
	for(i=j=l;i<=r;i++){
		while(color[di[j]])j++;
		if(color[di[i]]&&i>j)
			swap(di[i],di[j++]);
	}
	solve(l,j-1);solve(j,r);
}

inline void add(int u,int v,int f){e[++cnt]=Edge(v,f),nxt[cnt]=h[u],h[u]=cnt;}

void init(){
	read(n),read(m);
	int i,u,v,flow;
	for(i=1;i<=m;i++){
		read(u),read(v),read(flow);
		add(u,v,flow);add(v,u,flow);
	}
}

int main(){
	init();for(int i=1;i<=n;i++)di[i]=i;
	std:: random_shuffle(di+1,di+n+1);
	solve(1,n);printf("%d\n",ans.size());
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值