NowCoderL101E 水没都市 题解

分析

\(t\) 时刻还有城镇没有被淹没,\(t+1\) 时刻所有城镇将要被淹没。

一个思路是求出最后一个城镇被淹没的时刻 \(D\),然后通过魔法调整使得所有城镇至少会在 \(D\) 时刻被淹没。显然 \(D\) 就是 \(1\) 到每一个点所有的路径中的最大边权取最小值,猜都能猜到是最小生成树中的最大边权,证明略。

求出 \(D\) 后,我们的目标是使得 \(1 \rightarrow n\) 所有路径中最大边权最小为 \(D\),否则如果比 \(D\) 小则不满足条件,比 \(D\) 大则显然不优。

对于一条权值为 \(w\) 的边,代价为 \(D - w\),其中 \(w < D\)。考虑把 \((x,y,D-w)\) 加入图中,那么问题就变成了选择权值和最小的边,使得 \(1\)\(n\) 不连通。原因是如果 \(1\)\(n\) 不连通,那么 \(1 \rightarrow n\) 的所有路径中不存在 \(w < D\) 的边,这就达到了目的。

\(1\) 为源点,\(n\) 为汇点,求出图中的最小割即可。

注意这是一张无向图,加边的时候有一些细节。

CODE

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=30005, M=2e4+5, inf=1e15;
int n, m, D, fa[N], d[N];
int tot=1, h[N], hh[N], to[N<<1], nxt[N<<1], w[N<<1];
struct edge { int u, v, w; } e[M];
bool operator<(edge a,edge b) { return a.w<b.w; }
int get(int x) { return x==fa[x]? x:fa[x]=get(fa[x]); } 
void add(int x,int y,int z) {
	to[++tot]=y, w[tot]=z, nxt[tot]=h[x], h[x]=tot;
}
void addedge(int x,int y,int z) {
	add(x,y,z), add(y,x,z);
}
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
void kruskal() {
	for(int i=1;i<=n;++i) fa[i]=i;
	int cnt=0;
	sort(e+1,e+m+1);
	for(int i=1;i<=m;++i) {
		int x=get(e[i].u), y=get(e[i].v);
		if(x!=y) {
			fa[x]=y;
			D=max(D,e[i].w);
			if(++cnt==n-1) break;
		}
	}
}
bool bfs() {
	queue<int> q;
	memset(d,0,sizeof(d));
	d[1]=1, q.push(1);
	while(q.size()) {
		int x=q.front(); q.pop();
		hh[x]=h[x];
		for(int i=h[x];i;i=nxt[i]) {
			int y=to[i], z=w[i];
			if(d[y]||!z) continue;
			d[y]=d[x]+1;
			q.push(y);
			if(y==n) return 1; 
		}
	}
	return 0;
}
int dfs(int x,int flow) {
	if(x==n||!flow) return flow;
	int res=flow;
	for(int& i=hh[x];i;i=nxt[i]) {
		int y=to[i], z=w[i];
		if(d[y]!=d[x]+1||!z) continue;
		int k=dfs(y,min(res,z));
		if(!k) d[y]=0; else w[i]-=k, w[i^1]+=k, res-=k;
		if(!res) return flow;
	}
	return flow-res;
}
int dinic() {
	int ans=0;
	while(bfs()) ans+=dfs(1,inf);
	return ans;
}
signed main() {
	n=read(), m=read();
	for(int i=1;i<=m;++i) {
		e[i].u=read(), e[i].v=read(), e[i].w=read();
	} 
	kruskal();
	for(int i=1;i<=m;++i) {
		int u=e[i].u, v=e[i].v;
		if(D>e[i].w) addedge(u,v,D-e[i].w);
	}
	printf("%lld\n",dinic());
}

NowCoderL101E 水没都市 题解
https://yozora0908.github.io/2022/nc11247e-solution/
作者
yozora0908
发布于
2022年8月4日
许可协议