BZOJ 2521: [Shoi2010]最小生成树

题目大意:

每次操作可以让除了一条边之外的所有边-1,问让一条边必定出现在最小生成树上的代价是多少。

题解:

一条边必定出现在最小生成树上说明比它小的边不能把两点提前联通。对于比给定边小的边,割掉这条边的代价就要使得这条边比给定边权值来得大,目标是让给定边连接的两点不连通。

这就是最小割问题了

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int cnt,S,T,n,m,id,last[1000005],h[1000005],q[1000005],cur[1000005],x[1000005],y[1000005],z[1000005];
struct node{
	int to,next,cap;
}e[1000005];
void add(int a,int b,int c){
	e[++cnt].to=b;
	e[cnt].next=last[a];
	e[cnt].cap=c;
	last[a]=cnt;
}
void ADD(int a,int b,int c){
	add(a,b,c);
	add(b,a,c);
}
int bfs(){
	for (int i=1; i<=n; i++) h[i]=-1;
	h[S]=0;
	int head=0,tail=1;
	q[tail]=S;
	while (head<tail){
		int x=q[++head];
		for (int i=last[x]; i; i=e[i].next){
			int V=e[i].to;
			if (e[i].cap && h[V]==-1){
				h[V]=h[x]+1;
				q[++tail]=V;
			}
		}
	}
	return h[T]!=-1;
}
int dfs(int x,int low){
	if (x==T) return low;
	int used=0;
	for (int i=cur[x]; i; i=e[i].next){
		int V=e[i].to;
		if (e[i].cap && h[V]==h[x]+1){
			int w=dfs(V,min(e[i].cap,low-used));
			used+=w;
			e[i].cap-=w;
			e[i^1].cap+=w;
			cur[x]=i;
			if (used==low) return low;
		}
	}
	if (!used) h[x]=-1;
	return used;
}
int dinic(){
	int ans=0;
	while (bfs()){
		for (int i=1; i<=n; i++) cur[i]=last[i];
		ans+=dfs(S,1e9);
	}
	return ans;
}
int main(){
	cnt=1;
	scanf("%d%d%d",&n,&m,&id);
	for (int i=1; i<=m; i++)
		scanf("%d%d%d",&x[i],&y[i],&z[i]);
	for (int i=1; i<=m; i++){
		if (i==id) S=x[i],T=y[i];
		else if (z[i]<=z[id]) ADD(x[i],y[i],z[id]-z[i]+1);
	}
	printf("%d\n",dinic());
	return 0;
}

  

posted @ 2018-04-05 21:00  ~Silent  阅读(250)  评论(0编辑  收藏  举报
Live2D