# 【題解】JOI18 commuter pass
## 題意
>有 $n$ 點 $m$ 邊的帶權無向圖($n\le 10^5; m \le 2\times10^5$)。你可以指定一條 $s-t$ 的最短路使其邊權歸零,求 $u-v$ 的最短路徑。
## 作法
將無向圖拆成有向圖,可以得到 $stDAG$ 的任何完整路徑(從入度為零的點開始,走到出度為零的點為止)都是 $s-t$ 最短路。
$u-v$ 最短路有兩種可能:
1. 不經過 $s-t$ 最短路,$ans = dis(u,v)$
2. $u$ 從 $x$ 接入 $stDAG$,從 $y$ 離開 $stDAG$ 前往 $v$,$ans = dis(u,x)+dis(y,v)$
3. $v$ 從 $x$ 接入 $stDAG$,從 $y$ 離開 $stDAG$ 前往 $u$,$ans = dis(v,x)+dis(y,u)$
找出 $dis(v,x)$ 和 $dis(y,u)$ 只要從 $u,v$ 分別作 Dijkstra 即可。
### 如何找到 $x, y$ ?
考慮在 $stDAG$ 上 DP:
* $dpu[i]$ 代表 $path(s,i)$ 上最小的 $dis(u,i)$
* $dpv[i]$ 代表 $path(s,i)$ 上最小的 $dis(v,i)$
則將 $i$ 作為 $y$ 的答案即為 $min(dpu[prev]+dis(v,i), dpv[prev]+dis(u,i))$。
### 如何在 $stDAG$ 上 DP?
**對於正邊權圖,只要維護 $vis$ 使得每個點只會被拿出來一次,Dijkstra 拿出來的順序就是在單源最短路 DAG 上的拓樸排序。**
因此我們可以從 $s$ 做一次 Dijkstra ,在 $sDAG$ 上得到 $dpu$ 和 $dpv$。
若我們在 $sDAG$ 的反圖 $sDAG'$ 上從 $t$ 做 BFS 可以得到 $tsDAG$。此時的前驅 $i$ 就會是 $y$,後繼 $j$ 就會是 $x$,套用前面方法求 $ans$ 即可。
BFS 不用另外寫,直接和 Dijkstra 合併即可(被拿出來的順序不重要)。
## AC Code
```cpp
#include <bits/stdc++.h>
#define int long long
#define rep(i,j,k) for (int i=j; i<=k; i++)
#define pb push_back
#define lyx_my_wife ios::sync_with_stdio(0), cin.tie(0);
using namespace std;
typedef pair<int,int> pii;
const int N = 1e5+10, M = 2e5+10;
int n, m, s, t, u, v;
vector<pii> G[N];
int disu[N], disv[N], diss[N], dist[N], vis[N];
int dpu[N], dpv[N], ans;
void dijks(int rt, int dis[N], int fordp=0, int forans=0)
{
memset(dis, 0x3f, sizeof(disu));
memset(vis, 0, sizeof(vis));
priority_queue<pii, vector<pii>, greater<pii>> pq;
dis[rt] = 0;
pq.push({0, rt});
if (fordp)
{
memset(dpu, 0x3f, sizeof(dpu));
memset(dpv, 0x3f, sizeof(dpv));
}
while(pq.size())
{
int i = pq.top().ss, d = pq.top().ff;
pq.pop();
if (vis[i]) continue;
vis[i] = 1;
if (fordp)
{
dpu[i] = min(dpu[i], disu[i]);
dpv[i] = min(dpv[i], disv[i]);
}
for (auto e: G[i])
{
int j = e.ss, w = e.ff;
if (forans)
{
if (diss[j] != diss[i]-w) continue;
ans = min(ans, dpu[j]+disv[i]);
ans = min(ans, dpv[j]+disu[i]);
}
if (dis[j] > d+w)
{
dis[j] = d+w;
pq.push({dis[j], j});
if (fordp) dpu[j] = dpu[i], dpv[j] = dpv[i];
}
else if (dis[j] == d+w)
{
if (fordp) dpu[j] = min(dpu[j], dpu[i]), dpv[j] = min(dpv[j], dpv[i]);
}
}
}
}
signed main()
{
lyx_my_wife
cin >> n >> m >> s >> t >> u >> v;
rep(_,1,m)
{
int i, j, w;
cin >> i >> j >> w;
G[i].pb({w,j});
G[j].pb({w,i});
}
dijks(u,disu);
dijks(v,disv);
dijks(s,diss,1);
ans = disu[v];
dijks(t,dist,0,1);
cout << ans << '\n';
}
```