带负环的全源最短路(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;
}

浙公网安备 33010602011771号