BZOJ 4519: [Cqoi2016]不同的最小割 最小割树 / 分治最小割

4519: [Cqoi2016]不同的最小割

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 763  Solved: 450
[Submit][Status][Discuss]

Description

学过图论的同学都知道最小割的概念:对于一个图,某个对图中结点的划分将图中所有结点分成
两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割。对于带权图来说,将
所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而s,t的最小割指的是在
关于s,t的割中容量最小的割。
而对冲刺NOI竞赛的选手而言,求带权图中两点的最小割已经不是什么难事了。我们可以把
视野放宽,考虑有N个点的无向连通图中所有点对的最小割的容量,共能得到N(N−1)
2个数值。
这些数值中互不相同的有多少个呢?这似乎是个有趣的问题。

Input

输入文件第一行包含两个数N,M,表示点数和边数。接下来M行,每行三个数u,v,w,
表示点u和点v(从1开始标号)之间有条边权值是w。
1<=N<=850 1<=M<=8500 1<=W<=100000

Output

 输出文件第一行为一个整数,表示个数。

Sample Input

4 4
1 2 3
1 3 6
2 4 5
3 4 4

Sample Output

3

和上篇blog基本一样传送门

个数用set维护一下就OK了


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
using namespace std;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=10*x+ch-'0';ch=getchar();}
	return x*f;
}
inline void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=900,M=20000,inf=0X3f3f3f3f;

int ecnt=1,n,a[N],last[N];
struct EDGE{int to,nt,val;}e[M];
inline void add(int u,int v,int val)
{e[++ecnt]=(EDGE){v,last[u],val};last[u]=ecnt;}

int S,T,q[N],d[N];
bool bfs()
{
	memset(d,0,sizeof(d));
	int head=0,tail=1;q[0]=S,d[S]=1;
	while(head<tail)
	{
		int u=q[head++];
		for(int i=last[u];i;i=e[i].nt)
		if(e[i].val&&!d[e[i].to])
		{
			d[e[i].to]=d[u]+1;
			q[tail++]=e[i].to;
		}
	}
	return d[T];
}

int dfs(int u,int lim)
{
	if(u==T||!lim)return lim;
	int res=0;
	for(int tmp,i=last[u];i;i=e[i].nt)
	if(d[e[i].to]==d[u]+1)
	{
		tmp=dfs(e[i].to,min(e[i].val,lim));
		lim-=tmp;res+=tmp;e[i].val-=tmp;e[i^1].val+=tmp;
		if(!lim)break;
	}
	if(!res)d[u]=-1;
	return res;
}

void restore()
{for(int i=2;i<=ecnt;i+=2)e[i].val=e[i^1].val=(e[i].val+e[i^1].val)>>1;}

bool vis[N];
void dfs(int u)
{vis[u]=1;for(int i=last[u];i;i=e[i].nt)if(!vis[e[i].to]&&e[i].val)dfs(e[i].to);}

int tmp[N];
set<int> s;
void solve(int l,int r)
{
	if(l==r)return ;
	restore();S=a[l],T=a[r];
	int mf=0;while(bfs())mf+=dfs(S,inf);
	memset(vis,0,sizeof(vis));dfs(S);
	s.insert(mf);int L=l,R=r;
	for(int i=l;i<=r;++i)if(vis[a[i]])
	tmp[L++]=a[i];else tmp[R--]=a[i];
	for(int i=l;i<=r;++i)a[i]=tmp[i];
	solve(l,L-1);solve(R+1,r);
}

int main()
{
	n=read();int m=read();
	while(m--)
	{
		int u=read(),v=read(),val=read();
		add(u,v,val);add(v,u,val);
	}
	for(int i=1;i<=n;i+=3)a[i]=i,a[i+1]=i+1,a[i+2]=i+2;
	solve(1,n);print(s.size());puts("");return 0;
}
/*
4 4
1 2 3
1 3 6
2 4 5
3 4 4

3
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值