移动端没做适配,所以看起来十分诡异是正常的

【模板】Johnson最短路_luoguP5905

算法作用

用来解决带负权的有向图的最短路问题。

只要跑一次spfa,就可以随便跑Dij了。

算法思想

  • 给每条边重新安排一个边权,使得不再存在负权边,并且可以由新图的最短路结果快速推出原图的最短路结果。

  • 不连通的对每个连通块可以分别求。所以我们只要考虑联通的情况下怎么做。

  • 那么,我们可以回想一下k短路,在哪个里面我们有最短路树。而且图中非树边我们给他们赋予了一个新的权值,意义是如果加上这条边,最短路会变大多少。我们发现,这个值一定是正的。

  • 然后,就很好玩了。你会发现这个东西,很裂项。因为他的表达式是\(w_e+dis_{e_{from}}-dis_{e_{to}}\)

  • 于是,就发现,所有\(i\)\(j\)的路径,都是原来的路径长度再加上\(dis_i-dis_j\)

  • 赢了。

代码

luoguP5905

#include<bits/stdc++.h>
#define LL long long
#define MAXN 3100
#define MAXM 6100
#define MAXNUM 10000000
#define INF 1000000000
using namespace std;
template<typename T>void Read(T &cn)
{
	char c;int sig = 1;
	while(!isdigit(c = getchar()))if(c == '-')sig = -1; cn = c-48;
	while(isdigit(c = getchar()))cn = cn*10+c-48; cn*=sig;
}
template<typename T>void Write(T cn)
{
	if(cn < 0) {putchar('-'); cn = 0-cn; }
	int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
	while(cn)cm = cm*10+cn%10,cn/=10,wei++;
	while(wei--)putchar(cm%10+48),cm/=10;
	putchar(cx+48);
}
struct qwe{
	int a,b,ne,w;
	void mk(int cn, int cm, int cx, int cw) {a = cn; b = cm; ne = cx; w = cw; }
};
struct qwer{
	int a,b;
	void mk(int cn, int cm) {a = cn; b = cm; }
	inline friend bool operator <(qwer a, qwer b) {return a.b > b.b; }
};
qwe a[MAXM+MAXN+1];
int alen;
int head[MAXN+10];
int n,m,p;
int vis[MAXN+1], lu[MAXN+1], dui[MAXNUM+1], l, r;
int ci[MAXN+1];
int lu2[MAXN+1];
qwer dui2[MAXM+MAXN+1];
void lian(int cn, int cm, int cx) {a[++alen].mk(cn,cm,head[cn],cx); head[cn] = alen; }
void spfa(int cn)
{
	l = r = 0;
	memset(vis,0,sizeof(vis)); memset(ci,0,sizeof(ci));
	dui[++r] = cn;
	vis[0] = 1;
	while(l < r)
	{
		int dang = dui[++l]; vis[dang] = 0;
		ci[dang]++; if(ci[dang] > n) {p = 1; return; }
		for(int i = head[dang];i;i = a[i].ne)
		{
			int y = a[i].b;
			if(lu[y] <= lu[dang] + a[i].w) continue;
			lu[y] = lu[dang] + a[i].w;
			if(!vis[y]) dui[++r] = y, vis[y] = 1;
		}
	}
}
void dij(int cn, qwer dui[], int lu[])
{
	for(int i = 1;i<=n;i++) lu[i] = INF;
	lu[cn] = 0; int dlen = 0; dui[++dlen].mk(cn,0);
	while(dlen)
	{
		while(dlen && dui[1].b != lu[dui[1].a]) pop_heap(dui+1,dui+(dlen--)+1);
		if(!dlen) break;
		int dang = dui[1].a; pop_heap(dui+1,dui+(dlen--)+1);
		for(int i = head[dang];i;i = a[i].ne)
		{
			int y = a[i].b;
			if(lu[y] <= lu[dang] + a[i].w || !y) continue;
			lu[y] = lu[dang] + a[i].w;
			dui[++dlen].mk(y,lu[y]); push_heap(dui+1,dui+dlen+1);
		}
	}
}
int main()
{
	Read(n); Read(m);
	alen = 0; memset(vis,0,sizeof(vis));
	for(int i = 1;i<=m;i++) {int bx,by,bz; Read(bx); Read(by); Read(bz); lian(bx,by,bz); }
	p = 0; for(int i = 1;i<=n;i++) lu[i] = INF;  
	for(int i = 1;i<=n;i++) {if(lu[i] == INF) lu[i] = 0, spfa(i); if(p) {puts("-1"); return 0; } }
	for(int i = 1;i<=m;i++) a[i].w += lu[a[i].a] - lu[a[i].b];
	for(int i = 1;i<=n;i++)
	{
		dij(i,dui2,lu2);
		LL ans = 0;
		for(int j = 1;j<=n;j++) ans = ans + 1ll*j*(lu2[j] == INF ? INF : (lu2[j] - lu[i] + lu[j]));
		Write(ans); puts("");
	}
	return 0;
}
posted @ 2020-02-19 19:53  czyarl  阅读(114)  评论(0编辑  收藏  举报