P3385 【模板】负环 题解

差分约束系统是什么?

差分约束系统指的是一个序列 \(x={x_1,x_2,\cdots x_m}\) 和以如下形式出现的 \(n\) 元一次不等式组。

\[\left\{\begin{matrix} x_{i_1}-x_{j_1}\le c_{k_1} \\x_{i_2}-x_{j_2}\le c_{k_2} \\\vdots \\x_{i_n}-x_{j_n}\le c_{k_n} \end{matrix}\right. \]


还是说说前置知识。

P3385 【模板】负环

Description

给你一个 \(n\) 个点的有向图,让你判断图中是否有能从 \(1\) 号点出发可以到达的负环。

负环定义为一条边权和为负的回路。

Solution

思考负环的一些性质。

容易发现负环上一点与任意一点的最短路都不存在,因为你可以跑无数遍负环,边权和会越来越大。我们需要做的是找出一个限度值,如果超过了这个限度值就知道出现了负环。

注意到跑完一次单源最短路最多也就是把所有节点全部入队更新。如果点数为 \(n\),同一个节点最多只可能入队 \(n-1\) 次。

所以,开一个数组 cnt 记录每个节点入队的次数,如果次数超过 \(n-1\) 就说明有负环。

SPFA 在卡满情况下与朴素 Bellman-Ford 复杂度都为 \(O(nm)\)

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