[SHOI2010]最小生成树

题目

首先让其余所有边都减\(1\)和让自己加\(1\)没什么区别

考虑\(kruskal\)的过程

首先边权大于这条边的是不用考虑的

考虑把那些边权比这条边小的调节到比这条边大,这样就相当于在生成树上去掉了这条边(被这条边取代了)

至于调大到多少自然是使得边权恰好大\(1\)

让这条边必然存在就一定得让这条边链接的两个点不连通,于是我们把在生成树上去掉这条边看成割断这条边,于是这就是一个最小割

代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=1005;
const int inf=999999999;
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
std::queue<int> q;
struct E{int v,nxt,f;}e[maxn*4];
struct Ed{int u,v,w,o;}E[maxn];
inline int cmp(Ed A,Ed B) {if(A.w==B.w) return A.o<B.o;return A.w<B.w;}
int num=1,n,m,S,T,lb;
int head[maxn],d[maxn],cur[maxn];
inline void C(int x,int y,int f) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;e[num].f=f;}
inline void add(int x,int y,int f) {C(x,y,f),C(y,x,0);}
inline int BFS() {
    for(re int i=1;i<=n;i++) d[i]=0,cur[i]=head[i];
    q.push(S),d[S]=1;
    while(!q.empty()) {
        int k=q.front();q.pop();
        for(re int i=head[k];i;i=e[i].nxt)
        if(!d[e[i].v]&&e[i].f) d[e[i].v]=d[k]+1,q.push(e[i].v);
    }
    return d[T];
}
int dfs(int x,int now) {
    if(x==T||!now) return now;
    int flow=0,ff;
    for(re int& i=cur[x];i;i=e[i].nxt)
    if(d[e[i].v]==d[x]+1) {
        ff=dfs(e[i].v,min(now,e[i].f));
        if(ff<=0) continue;
        now-=ff,flow+=ff,e[i].f-=ff,e[i^1].f+=ff;
        if(!now) break;
    }
    return flow;
}
int main() {
	n=read(),m=read(),lb=read();
	for(re int i=1;i<=m;i++) E[i].u=read(),E[i].v=read(),E[i].w=read();
	E[lb].o=1;int t=E[lb].w;
	S=E[lb].u,T=E[lb].v;
	std::sort(E+1,E+m+1,cmp);
	for(re int i=1;i<=m;i++){
		if(E[i].o) break;
		add(E[i].u,E[i].v,t-E[i].w+1);
		add(E[i].v,E[i].u,t-E[i].w+1);
	}
	int ans=0;
	while(BFS()) ans+=dfs(S,inf);
	printf("%d\n",ans);
	return 0;
}
posted @ 2019-03-17 20:57  asuldb  阅读(151)  评论(0编辑  收藏  举报