把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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);
	}
}
posted @ 2021-05-26 19:29  275307894a  阅读(50)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end