[JZOJ5551] 【NOI2019模擬6.24】旅途【最短路】

Description

給出一個n個點m條邊的帶邊權無向圖
定義1到n的K最短路爲所有1到n的路徑中,路徑上的邊權前K大和的最小值。

求K=1~n的最短路。
n,m<=3000,邊權<=10^9

Solution

首先我們考慮枚舉第K大的邊,將小於這條邊的所有邊記爲0(相等的話強行給一個順序)
考慮一個樸素的DPf[i][j][0/1]f[i][j][0/1],表示當前到i號點,經過了j條大於枚舉這條邊的邊,是否經過這條邊。

直接分層圖轉移即可。

事實上我們完全不需要記第二維
枚舉第K大的邊e(可以是0),然後將所有邊權賦爲max(vve,0)max(v-v_e,0)

直接跑一遍最短路,用最後的距離加上veKv_e*K更新所有的答案。
正確性顯然,因爲原本的答案不會變,而不合法的部分只會更劣。

採用dijkstra,就在O(m2logm)O(m^2\log m)的時間複雜度解決。

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
const int N=3005;
typedef long long LL;
using namespace std;
int n,m,fs[N],nt[2*N],dt[2*N],pr[2*N],m1,lim;
void link(int x,int y,int z)
{
	nt[++m1]=fs[x];
	dt[fs[x]=m1]=y;
	pr[m1]=z;
}
LL ans[N],dis[N];
bool bz[N];
struct node
{
	int x;LL v;
	friend bool operator <(node x,node y)
	{
		return x.v>y.v;
	}
};
priority_queue<node> h;
int main()
{
	cin>>n>>m;
	memset(ans,107,sizeof(ans));
	fo(i,1,m) 
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		link(x,y,z),link(y,x,z);
	}
	fo(q,1,m+1)
	{
		lim=(q==m+1)?0:pr[2*q];
		memset(dis,107,sizeof(dis));
		memset(bz,0,sizeof(bz));
		dis[1]=0;
		while(!h.empty()) h.pop();
		h.push((node){1,0});
		fo(t,1,n-1)
		{
			while(bz[(h.top()).x]) h.pop();
			int k=(h.top()).x;
			bz[k]=1,h.pop();
			for(int i=fs[k];i;i=nt[i])
			{
				int p=dt[i];LL c=dis[k]+max(0,pr[i]-lim);
				if(dis[p]>c) dis[p]=c,h.push((node){p,c});
			}
		}
		fo(i,1,n) ans[i]=min(ans[i],(LL)i*lim+dis[n]);
	}
	fod(i,n,1) printf("%lld\n",ans[i]);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章