https://yukicoder.me/problems/no/807
前提知識
解説
https://yukicoder.me/submissions/327912
往復で1回だけ道のコストを0にできるということだが、双方向の無効グラフなので、
特に往復については考える必要はない。
重要なのは、「普通に頂点1から頂点iに移動する+頂点1から頂点iに1つの道のコストを0にして移動する」が答えであるということ。
普通に頂点1から頂点iに移動する
普通にダイクストラしよう。
頂点1から頂点iに1つの道のコストを0にして移動する
これもダイクストラで計算できる。
状態として、
(i, pr) := 頂点iにいて、コストを0にした移動を行ったかどうかpr
として最短距離を求める。
あとは、足し合わせたやつを答えるだけ。
int N, M; vector<pair<int, int>> E[101010]; //--------------------------------------------------------------------------------------------------- int vis[201010]; vector<ll> dijk() { vector<ll> D(N); vector<int> vis(N); rep(i, 0, N) D[i] = infl; rep(i, 0, N) vis[i] = 0; min_priority_queue<pair<ll, int>> que; D[0] = 0; que.push({ 0, 0 }); while (!que.empty()) { auto q = que.top(); que.pop(); ll cst = q.first; int cu = q.second; if (vis[cu]) continue; vis[cu] = 1; fore(p, E[cu]) { ll cst2 = cst + p.second; int to = p.first; if (chmin(D[to], cst2)) que.push({ D[to], to }); } } return D; } //--------------------------------------------------------------------------------------------------- vector<ll> dijk2() { vector<ll> D(N * 2); vector<int> vis(N * 2); rep(i, 0, N * 2) D[i] = infl; rep(i, 0, N * 2) vis[i] = 0; min_priority_queue<pair<ll, int>> que; D[0] = 0; que.push({ 0, 0 }); while (!que.empty()) { auto q = que.top(); que.pop(); ll cst = q.first; int cu = q.second / 2; int pr = q.second % 2; if (vis[q.second]) continue; vis[q.second] = 1; fore(p, E[cu]) { ll cst2 = cst + p.second; int to = p.first; if (chmin(D[to * 2 + pr], cst2)) que.push({ D[to * 2 + pr], to * 2 + pr }); if (pr == 0) { if (chmin(D[to * 2 + 1], cst)) que.push({ D[to * 2 + 1], to * 2 + 1 }); } } } return D; } //--------------------------------------------------------------------------------------------------- void _main() { cin >> N >> M; rep(i, 0, M) { int a, b, c; cin >> a >> b >> c; a--; b--; E[a].push_back({ b,c }); E[b].push_back({ a,c }); } auto d1 = dijk(); auto d2 = dijk2(); rep(i, 0, N) { ll ans = d1[i] + min(d2[i * 2], d2[i * 2 + 1]); printf("%lld\n", ans); } }