【bzoj2229】 Zjoi2011—最小割

http://www.lydsy.com/JudgeOnline/problem.php?id=2229 (題目連接)php

題意

  給出一張無向圖,$q$組詢問,每次詢問最小割不大於$c$的點對數量。html

Solution

  orz:DaD3zZios

  最小割樹什麼的好神,可是看不懂啊,不如直接擼代碼= =。根據網上神犇的理論,貌似最小割的數目不會超過$n-1$個,因此能夠將它構成一棵最小割樹。dom

  不過咱們的實現並不須要考慮怎麼構樹。直接暴力的話就是枚舉點對,要作$n^2$次$Dinic$,咱們經過選擇一些優秀的點對來減小$Dinic$的次數。每次分治,任選兩個在當前分治區間中的點做爲源點和匯點,在原圖上作一次$Dinic$,將原圖分爲了兩個割集$S$和$T$,更新$S$和$T$之間的點的最小割。將這兩個割集與分治區間取交獲得分值區間的割集$S'$和$T'$,而後遞歸處理$S'$和$T'$就能夠了。spa

  值得注意的是,這樣子並無減少問題的規模,只是經過有技巧的選擇源點和匯點來減小$Dinic$的次數(雖然我也不知道爲何這樣是正確的)。複雜度大概是$O(kn*Dinic)$,$k$這個常數應該不會太大,出題人總不會喪心病狂卡這玩意兒吧,大不了random_shuffle一下= =。htm

細節

  無向圖。blog

代碼

// bzoj2229
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf (1ll<<30)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std;

const int maxn=200,maxm=10010;
int Q,n,m,cnt,id[maxn],head[maxn],ans[maxn][maxn],vis[maxn],tmp[maxn];
struct edge {int to,next,w;}e[maxm];

namespace Dinic {
	int d[maxn],S,T;
	void link(int u,int v,int w) {
		e[++cnt]=(edge){v,head[u],w};head[u]=cnt;
		e[++cnt]=(edge){u,head[v],w};head[v]=cnt;
	}
	bool bfs() {
		memset(d,-1,sizeof(d));
		queue<int> q;q.push(S);d[S]=0;
		while (!q.empty()) {
			int x=q.front();q.pop();
			for (int i=head[x];i;i=e[i].next)
				if (e[i].w && d[e[i].to]<0) d[e[i].to]=d[x]+1,q.push(e[i].to);
		}
		return d[T]>0;
	}
	int dfs(int x,int f) {
		if (x==T || f==0) return f;
		int w,used=0;
		for (int i=head[x];i;i=e[i].next) if (e[i].w && d[e[i].to]==d[x]+1) {
				w=dfs(e[i].to,min(e[i].w,f-used));
				used+=w,e[i].w-=w,e[i^1].w+=w;
				if (used==f) return used;
			}
		if (!used) d[x]=-1;
		return used;
	}
	int main(int x,int y){
		S=x,T=y;int flow=0;
		while (bfs()) flow+=dfs(S,inf);
		return flow;
	}
}
using namespace Dinic;

void Init() {
	cnt=1;
	memset(head,0,sizeof(head));
	memset(ans,0x7f,sizeof(ans));
}
void dfs(int x) {
	vis[x]=1;
	for (int i=head[x];i;i=e[i].next)
		if (e[i].w && !vis[e[i].to]) dfs(e[i].to);
}
void solve(int L,int R) {
	if (L==R) return;
	for (int i=2;i<=cnt;i+=2) e[i].w=e[i^1].w=(e[i].w+e[i^1].w)>>1;
	int flow=Dinic::main(id[L],id[R]);
	memset(vis,0,sizeof(vis));dfs(id[L]);
	for (int i=1;i<=n;i++) {
		if (!vis[i]) continue;
		for (int j=1;j<=n;j++)
			if (!vis[j]) ans[i][j]=ans[j][i]=min(ans[i][j],flow);
	}
	int l=L,r=R;
	for (int i=L;i<=R;i++) vis[id[i]] ? tmp[l++]=id[i] : tmp[r--]=id[i];
	for (int i=L;i<=R;i++) id[i]=tmp[i];
	solve(L,l-1);solve(r+1,R);
}

int main() {
	int T;scanf("%d",&T);
	while (T--) {
		scanf("%d%d",&n,&m);Init();
		for (int i=1;i<=n;i++) id[i]=i;
		for (int u,v,w,i=1;i<=m;i++) {
			scanf("%d%d%d",&u,&v,&w);
			Dinic::link(u,v,w);
		}
		solve(1,n);
		scanf("%d",&Q);
		for (int c,i=1;i<=Q;i++) {
			scanf("%d",&c);int res=0;
			for (int j=1;j<=n;j++)
				for (int k=j+1;k<=n;k++) if (ans[j][k]<=c) res++;
			printf("%d\n",res);
		}
		puts("");
	}
	return 0;
}
相關文章
相關標籤/搜索