【題解】[JOI2018] Commuter Pass
阿新 • • 發佈:2021-10-04
比較套路的題目。
首先我們需要求出 \(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; }