带负环的全源最短路(Johnson 算法)

https://www.luogu.com.cn/problem/P5905

使用 \(spfa\) 求出初始势能,之后边权变成 \(w+h_u-h_v\) 一定非负,跑 \(dijkstra\) 即可。

为了处理负环,先虚拟出超级源点,向所有点连边权为 \(0\) 的边,从这个点开始求势能。可以通过将势能初始化为 \(0\) 的技巧不显式连边。

时间复杂度 \(\mathcal{O}(V(V+E)\log V)\)

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int INF = 1e9;

void solve(){
	int n,m;
	cin >> n >> m;

	vector<vector<pair<int,int>>> adj(n+1);
	for (int i=0;i<m;i++){
		int u,v,w;
		cin >> u >> v >> w;
		adj[u].emplace_back(w,v);
	}	

	vector<bool> vis(n+1,false);
	vector<int> h(n+1);
	vector<int> cnt(n+1);
	queue<int> q;

	for (int i=1;i<=n;i++){
		q.push(i);
		vis[i] = true;
	}

	while (!q.empty()){
		int u = q.front();
		q.pop();
		vis[u] = false;

		for (auto& [w,v]:adj[u]){
			if (h[u]+w<h[v]){
				h[v] = h[u]+w;
				if (!vis[v]){
					q.push(v);
					vis[v] = true;
				}

				if (++cnt[v]>=n){
					cout << -1 << '\n';
					return;
				}
			}
		}
	}

	vector<vector<int>> dis(n+1,vector<int>(n+1,INF));

	for (int s=1;s<=n;s++){
		priority_queue<pair<int,int>,vector<pair<int,int>>,greater<>> pq;
		dis[s][s] = 0;
		pq.emplace(0,s);

		while (!pq.empty()){
			auto [d,u] = pq.top();
			pq.pop();

			if (d>dis[s][u]) continue;
			for (auto& [w,v]:adj[u]){
				if (dis[s][u]+h[u]-h[v]+w<dis[s][v]){
					dis[s][v] = dis[s][u]+h[u]-h[v]+w;
					pq.emplace(dis[s][v],v);
				}
			}
		}

		for (int t=1;t<=n;t++){
			if (dis[s][t]!=INF) dis[s][t] += h[t]-h[s];
		}
	}

	for (int i=1;i<=n;i++){
		ll res = 0;
		for (int j=1;j<=n;j++){
			res += (ll)j*dis[i][j];
		}
		cout << res << '\n';
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t = 1;
	// cin >> t;
	while (t--) solve();

	return 0;
}
posted @ 2026-05-19 12:17  kzssCCC  阅读(11)  评论(0)    收藏  举报