P5960 【模板】差分约束 题解

P5960 【模板】差分约束

Description

给你一个差分约束系统,让你判断这个差分约束系统是否存在合法的解,如果存在,输出任意一组可行解。

\(1\le n,m \le 5\times 10^3\)

Solution

我们单独把一个式子拿出来看:

\[x_i-x_j\le c_k \]

移项:

\[x_i\le x_j+c_k \]

瞬间想起了一些不好的东西。

int v=e[i].to,w=e[i].w;
if(dis[v]>=dis[u]+w){
	dis[v]=dis[u]+w;
}

对没错这个东西很像最短路中的三角形不等式\(\text{dis[v]}\le \text{dis[u]+w}\)

考虑把三角形不等式稍作改动:\(\text{dis[i]}\le \text{dis[j]+k}\)。最终的最短路径中每条边都要满足这个不等式,而差分约束的最终可行解每个 \(x_i\) 也都要满足这个不等式。

因此,对于每一个不等式,我们考虑建一条从 \(j\)\(i\),长度为 \(c_k\) 的有向边。

我们先建立一个超级源点,不妨设为 \(0\) 号点。从 \(0\) 号点向所有点连一条长度为 \(0\) 的边,跑最短路即可。

(Tips:差分约束的式子也可以拿来跑最长路)

我们不清楚图的具体形态,所以要注意判断负环。当存在负环时(大多数时候使用 SPFA 来做),差分约束系统无解。

最短路结束之后,若差分约束系统有可行解,一组可行解即为 \(x_i=dis_i\)

复杂度就是 SPFA 的复杂度,最坏 \(O(nm)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
long long n,m,tot,head[10005],dis[10005],cnt[10005];
bool vis[10005];
struct node{
	int from,to,w,nxt;
}e[20005];
inline void add_edge(int u,int v,int w){
	e[++tot].from=u;
	e[tot].to=v;
	e[tot].w=w;
	e[tot].nxt=head[u];
	head[u]=tot;
	return;
}
queue<int>q;
inline bool SPFA(){
	vis[0]=1;
	q.push(0);
	memset(dis,63,sizeof(dis));
	dis[0]=0;
	while(!q.empty()){
		int x=q.front();
		q.pop();
		vis[x]=0;
		for(int i=head[x];i;i=e[i].nxt){
			int u=x,v=e[i].to,w=e[i].w;
			if(dis[v]>=dis[u]+w){
				dis[v]=dis[u]+w;
				if(!vis[v]){
					cnt[v]++;
					vis[v]=1;
					if(cnt[v]>n){
						return 1;
					}
					q.push(v);
				}
			}
		}
	}
	return 0;
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		add_edge(0,i,0);
	}
	for(int i=1;i<=m;i++){
		int x,y,k;
		cin>>x>>y>>k;//x-y<=k   ->   x<=y+k
		add_edge(y,x,k);
	}
	if(SPFA()){
		cout<<"NO"<<endl;
	}
	else{
		for(int i=1;i<=n;i++){
			cout<<dis[i]<<" ";
		}
	}
	return 0;
}
posted @ 2025-12-09 22:08  Creativexz  阅读(3)  评论(0)    收藏  举报