1. 程式人生 > >[bzoj2229][Zjoi2011]最小割_網絡流_最小割樹

[bzoj2229][Zjoi2011]最小割_網絡流_最小割樹

memset void 等於 geo == else 並且 bzoj 取出

最小割 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;
}

小結:好東西啊。

[bzoj2229][Zjoi2011]最小割_網絡流_最小割樹