[bzoj2229][Zjoi2011]最小割_网络流_最小割树

最小割 bzoj-2229 Zjoi-2011

题目大意:题目链接

注释:略。


想法:

在这里给出最小割树的定义。

最小割树啊,就是这样一棵树。一个图的最小割树满足这棵树上任意两点之间的最小值就是原图中这两点之间的最小割。

这个性质显然是非常优秀的。

我们不妨这样假设,我么已经把最小割树求出来了,那么这个题就迎刃而解了。

我们可以直接枚举点对,然后暴力验证就可以直接枚举出所有的合法点对是吧。

那么问题来了,我们如何才能求出所有的合法的点对?

这就需要用到了最小割树的构建过程。

我们最小割树的构建方式是分治构建的。

也就是说:

我们每次直接随意取出两个点然后在原图中求出这两个点的最小割。

并且在这两个点之间连一条等于最小割大小的边。

之后我们对于原图把所有和S相连的分到一侧,把所有和T相连的分到另一侧。

递归分治即可。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define N 155
using namespace std;

int cnt,n,m,dis[N],last[N],a[N],tmp[N],ans[N][N],s,t,mark[N];
struct edge{int to,c,next;}e[N*200];
queue <int> q;

void addedge(int u,int v,int c)
{
	e[++cnt].to=v;e[cnt].c=c;e[cnt].next=last[u];last[u]=cnt;
	e[++cnt].to=u;e[cnt].c=c;e[cnt].next=last[v];last[v]=cnt;
}

bool bfs()
{
	memset(dis,0,sizeof(dis));
	dis[s]=2;
	while (!q.empty()) q.pop();
	q.push(s);
	while (!q.empty())
	{
		int u=q.front();
		q.pop();
		for (int i=last[u];i;i=e[i].next)
			if (e[i].c&&!dis[e[i].to])
			{
				dis[e[i].to]=dis[u]+1;
				if (e[i].to==t) return 1;
				q.push(e[i].to);
			}
	}
	return 0;
}

int dfs(int x,int maxf)
{
	if (x==t||!maxf) return maxf;
	int ret=0;
	for (int i=last[x];i;i=e[i].next)
		if (e[i].c&&dis[e[i].to]==dis[x]+1)
		{
			int f=dfs(e[i].to,min(e[i].c,maxf-ret));
			e[i].c-=f;
			e[i^1].c+=f;
			ret+=f;
			if (ret==maxf) break;
		}
	if (!ret) dis[x]=0;
	return ret;
}
void dfs(int x)
{
	mark[x]=1;
	for (int i=last[x];i;i=e[i].next)
		if (e[i].c&&!mark[e[i].to]) dfs(e[i].to);
}
void solve(int l,int r)
{
	if (l==r) return;
	s=a[l];t=a[r];
	for (int i=2;i<=cnt;i+=2)
		e[i].c=e[i^1].c=(e[i].c+e[i^1].c)/2;
	int flow=0;
	while (bfs()) flow+=dfs(s,inf);
	memset(mark,0,sizeof(mark));
	dfs(s);
	for (int i=1;i<=n;i++)
		if (mark[i])
			for (int j=1;j<=n;j++)
				if (!mark[j])
					ans[i][j]=ans[j][i]=min(ans[i][j],flow);
	int i=l,j=r;
	for (int k=l;k<=r;k++)
		if (mark[a[k]]) tmp[i++]=a[k];
		else tmp[j--]=a[k];
	for (int k=l;k<=r;k++)
		a[k]=tmp[k];
	solve(l,i-1);
	solve(j+1,r);
}

int main()
{
	int cas;
	scanf("%d",&cas);
	while (cas--)
	{
		scanf("%d%d",&n,&m);
		cnt=1;
		for (int i=1;i<=n;i++)
			a[i]=i;
		memset(last,0,sizeof(last));
		memset(ans,inf,sizeof(ans));
		for (int i=1;i<=m;i++)
		{
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			addedge(x,y,z);
		}
		solve(1,n);
		int q;
		scanf("%d",&q);
		for (int i=1;i<=q;i++)
		{
			int x,tot=0;
			scanf("%d",&x);
			for (int i=1;i<n;i++)
				for (int j=i+1;j<=n;j++)
					if (ans[i][j]<=x) tot++;
			printf("%d\n",tot);
		}
		cout<<endl;
	}
	return 0;
}

 小结:好东西啊。

posted @ 2019-01-09 15:54  JZYshuraK_彧  阅读(213)  评论(0编辑  收藏  举报