CF1450E Capitalism - 差分约束系统 - 最短路 -

题目链接:https://codeforces.com/contest/1450/problem/E

题解:
题目中的等式关系为 \(a_u-a_v=1\)\(|a_u-a_v|=1\)
首先,等式关系不好处理,考虑化成不等式
第一种:\(a_u-a_v\leq 1\)\(a_v-a_u\leq -1\)
第二种:\(a_u-a_v\leq 1\)\(a_v-a_u\leq 1\)\(a_u \neq a_v\)
发现这就是三角形不等式,可以将 \(a_u\) 等价于源点到 \(u\) 的最短距离
\(a_u-a_v \leq c\) 就可以等价于 \((v,u)\) 连了一条长度为 \(c\) 的有向边
因此可以将等式关系对应的图建出来。
此时注意到,如果 \(a_u=a_v\) 的话,一、二种条件均不满足,此时无解。又注意到 \((u,v)\) 是原图的边,因此这个条件就可以等价于原图不是二分图则无解
同样的,如果图中有负环的话,显然也是无法构造出来 \(a\)

如何让极差最大?一个重要的观察是发现极差是有上界的,因为 \(a_u-a_v\leq dist(v,u)\)(否则显然可以更新 \(a_u\))。那这个上界能否取到呢?如果我们把 \(v\) 当做源点跑最短路,那么 \(a_u=dist(v,u), a_v=0\),此时就能取到上界。因此跑 floyd,找到 \(dist\) 最大的 \((v,u)\) ,那么将 \(v\) 当做源点,\(a_i=dist(v,i)\) 即可

小细节:判负环可以直接用 \(dist(i,i)<0\),判二分图可以用 \(dist(s,u)=dist(s,v)\)\((u,v) \in E\)

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 205;

int n,m;
vector<pii>g[maxn];
int dis[maxn][maxn];

signed main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int x,y,b;scanf("%d%d%d",&x,&y,&b);
		// a_y=a_x+1 -> a_y-a_x <= 1 && a_x-a_y <= -1
		if(b == 0)g[x].pb(mpr(y, 1)), g[y].pb(mpr(x, 1));
		else g[x].pb(mpr(y, 1)), g[y].pb(mpr(x, -1));
	}
	memset(dis,0x3f,sizeof dis);
	for(int i=1;i<=n;i++)
		dis[i][i] = 0;
	for(int i=1;i<=n;i++)
		for(auto j : g[i])dis[i][j.first] = j.second;
	
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
	for(int i=1;i<=n;i++)
		if(dis[i][i] < 0)	// 判负环 
			return puts("NO"), 0;
	
	int res = -INF, r;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			for(auto k : g[j])
				if(dis[i][k.first] == dis[i][j])	// 判二分图 
					return puts("NO"), 0;
			if(dis[i][j] > res)
				res = dis[i][j],
				r = i;
		}
	puts("YES");
	int mx = -INF, mn = INF;
	for(int i=1;i<=n;i++)mx = max(mx, dis[r][i]), mn = min(mn, dis[r][i]);
	printf("%d\n",mx-mn);
	int ans = 0;
	for(int i=1;i<=n;i++)
		printf("%d ",dis[r][i]);
	
	return 0;
}
posted @ 2023-06-24 16:35  SkyRainWind  阅读(6)  评论(0编辑  收藏  举报