CF689B Mike and Shortcuts
阿新 • • 發佈:2021-10-31
水題解的一天。
題目大意
給定 \(n\) 個點 任意兩點 \(i,j\) 間有權值為 \(|i-j|\) 的雙向邊,另有 \(n\) 條權值為 \(1\) 的單向邊,求以 \(1\) 為起點的單源最短路徑。
\(1\leqslant n\leqslant200000\)
解題思路
由於 \(n\) 太大了,\(n^2\) 條邊不能接受。
考慮簡化, 發現 \(i \to \ k\) 之間的邊可以由 \(\forall i \leq j<k,j \to j+1\) 這些權值為 \(1\) 的邊拼成。
然後跑 dij
最短路即可
總時間複雜度是 \(\mathcal{O}(n\log n)\)。
CODE
#include <bits/stdc++.h> using namespace std; const int _ = 200005; int tot, head[_], to[_ * 3], nxt[_ * 3], w[_ * 3]; int vis[_], dis[_]; void add(int x, int y, int z) { to[++tot] = y; w[tot] = z; nxt[tot] = head[x]; head[x] = tot; } void dij() { priority_queue<pair<int, int>> q; memset(dis, 0x3f, sizeof dis); q.push(make_pair(0, 1)); dis[1] = 0; while (!q.empty()) { int x = q.top().second; q.pop(); if (vis[x]) continue; vis[x] = 1; for (int i = head[x]; i; i = nxt[i]) { int y = to[i], z = w[i]; if (dis[y] > dis[x] + z) { dis[y] = dis[x] + z; q.push(make_pair(-dis[y], y)); } } } } int n; signed main() { scanf("%d", &n); for (int i = 1; i < n; i++) { add(i, i + 1, 1); add(i + 1, i, 1); } for (int i = 1; i <= n; i++) { int x; scanf("%d", &x); add(i, x, 1); } dij(); for (int i = 1; i <= n; i++) printf("%d ", dis[i]); return 0; }
本文來自部落格園,作者:蒟蒻orz,轉載請註明原文連結:https://www.cnblogs.com/orzz/p/15489174.html