1. 程式人生 > 其它 >【題解】[JOI2018] Commuter Pass

【題解】[JOI2018] Commuter Pass

比較套路的題目。

首先我們需要求出 \(S\to T\) 的最短路可行邊。

我們分別以 \(S/T\) 為起點,跑最短路得到 \(dist_1,dist_2\) ,如果兩端 \(dist\) 之和加上邊的長度為最短路,則當前邊出現在一條最短路中。

現在從 \(U\to V\) ,免費的一定是一條連續的路徑。

而這條免費的路徑要麼是順著最短路徑圖走一段,要麼是逆著最短路徑圖走一段,而不能同時有順向或逆向走。

簡單想了一個貪心,大概是先求出 \(U/V\) 到最短路徑圖的最近點 \(P,Q\),再從 \(P/Q\) 為起點出發順向或逆向找到最近公共可達點,分別以 \(P,Q\) 為起點正圖反圖都跑一遍最短路。

碼了 \(10\) 分鐘感覺太麻煩了,不如直接分層圖。第 \(1\) 層和第 \(4\) 層分別表示還未開始免費路徑,已經結束免費路徑。第 \(2\) 層表示免費路徑順著走,這一層就是最短路圖,第 \(3\) 層表示免費路徑逆著走,就是最短路圖的反圖。

最後再跑一遍最短路即可,一共跑三遍最短路,時間複雜度 \(\mathcal{O}((N+M)\log (N+M))\)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 400005
#define int long long 
using namespace std;
int n,m,d[N],c[N],mk[N],h[N],tot,s,t,u,v;
struct edge{int to,nxt,val;}e[N];
void add(int x,int y,int z){e[++tot].nxt=h[x];h[x]=tot;e[tot].to=y;e[tot].val=z;}
typedef pair<int,int> Pr;
priority_queue<pair<int,int>>q;
vector<Pr>a[N];
void ins(int x,int y,int z){a[x].push_back(make_pair(y,z));}
void dij(){
	memset(d,0x3f,sizeof(d));
	d[s]=0;q.push(make_pair(0,s));
	while(!q.empty()){
		int x=q.top().second;q.pop();mk[x]=1;
		for(int i=h[x];i;i=e[i].nxt)if(d[x]+e[i].val<d[e[i].to])
			d[e[i].to]=d[x]+e[i].val,q.push(make_pair(-d[e[i].to],e[i].to));
		while(!q.empty()&&mk[q.top().second])q.pop();
	}
	memset(c,0x3f,sizeof(c));
	memset(mk,0,sizeof(mk));
	c[t]=0;q.push(make_pair(0,t));
	while(!q.empty()){
		int x=q.top().second;q.pop();mk[x]=1;
		for(int i=h[x];i;i=e[i].nxt)if(c[x]+e[i].val<c[e[i].to])
			c[e[i].to]=c[x]+e[i].val,q.push(make_pair(-c[e[i].to],e[i].to));
		while(!q.empty()&&mk[q.top().second])q.pop();
	}
	rep(x,1,n)
		for(int i=h[x];i;i=e[i].nxt)
			if(d[x]+e[i].val+c[e[i].to]==d[t]){
				ins(x+n,e[i].to+n,0),ins(e[i].to+n+n,x+n+n,0);
			}
}
void solve(){
	memset(d,0x3f,sizeof(d));
	memset(mk,0,sizeof(mk));
	d[u]=0;q.push(make_pair(0,u));
	while(!q.empty()){
		int x=q.top().second;q.pop();mk[x]=1;
		for(auto w:a[x]){
			int y = w.first,z = w.second;
			if(d[x] + z < d[y])d[y] = d[x] + z,q.push(make_pair(-d[y],y));
		}
		while(!q.empty()&&mk[q.top().second])q.pop();
	}
	printf("%lld\n",d[v+n+n+n]);
}
signed main(){
	scanf("%lld%lld",&n,&m);
	scanf("%lld%lld%lld%lld",&s,&t,&u,&v);
	rep(i,1,m){
		int x,y,z;scanf("%lld%lld%lld",&x,&y,&z);
		add(x,y,z);add(y,x,z);
		ins(x,y,z);ins(y,x,z);
		ins(x+n+n+n,y+n+n+n,z);ins(y+n+n+n,x+n+n+n,z);
	}
	rep(i,1,n)ins(i,i+n,0),ins(i,i+n+n,0),ins(i+n,i+n+n+n,0),ins(i+n+n,i+n+n+n,0);
	dij();solve();
	return 0;
}