luogu P5905 【模板】Johnson 全源最短路
题面传送门
因为这道题点名卡spfa,所以要用spfa。
本来可以跑\(n\)轮spfa但是这个可能有负权边。
然而这个东西如果我们以\(0\)为超级源跑一边最短路设为\(h_i\),那么显然有\(h_i+w_{i,j}\geq h_j\)
所以我们更改每条边权值为\(h_i+w_{i,j}-h_j\)因为这个只和两边有关系不难证明它是对的。
然后直接跑即可。时间复杂度\(O(n^2logn)\)
code:
#include <vector>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define l(x) x<<1
#define r(x) x<<1|1
#define re register
#define ll long long
#define db long double
#define N 3000
#define eps (1e-6)
#define mod 1000000007
using namespace std;
int n,m,k,x,y,z,h[N+5],in[N+5],fl[N+5],st[N+5],head;ll ans;
struct ques{
int to,w;
bool operator <(const ques &x)const{return w>x.w;}
};
struct yyy{int to,w;};vector<yyy> s[N+5];
queue<int> qs;priority_queue<ques> q;
int main(){
// freopen("1.in","r",stdin);freopen("1.out","w",stdout);
re int i,j,d[N+5],now;re ques cur;re yyy tmp;scanf("%d%d",&n,&m);for(i=1;i<=m;i++) scanf("%d%d%d",&x,&y,&z),s[x].push_back((yyy){y,z});
for(i=1;i<=n;i++) s[0].push_back((yyy){i,0});memset(h,0x3f,sizeof(h));h[0]=0;qs.push(0);fl[0]=1;
while(!qs.empty()){
now=qs.front();qs.pop();in[now]++;fl[now]=0;if(in[now]>2*n){printf("-1\n");return 0;}
for(i=0;i<s[now].size();i++)tmp=s[now][i],h[tmp.to]>h[now]+tmp.w&&(!fl[tmp.to]&&(qs.push(tmp.to),fl[tmp.to]=1),h[tmp.to]=h[now]+tmp.w);
}
for(i=1;i<=n;i++){
for(j=0;j<s[i].size();j++)tmp=s[i][j],s[i][j].w+=h[i]-h[tmp.to];in[i]=s[i].size();
}
for(i=1;i<=n;i++){
memset(d,0x3f,sizeof(d));d[i]=0;q.push((ques){i,0});
while(!q.empty()){
cur=q.top();q.pop();if(d[cur.to]^cur.w)continue;
for(j=0;j<in[cur.to];j++) tmp=s[cur.to][j],d[tmp.to]>d[cur.to]+tmp.w&&(d[tmp.to]=d[cur.to]+tmp.w,q.push((ques){tmp.to,d[tmp.to]}),0);
}
for(ans=0,j=1;j<=n;j++) ans+=(ll)(d[j]>1e9?1e9:d[j]+h[j]-h[i])*j;printf("%lld\n",ans);
}
}