Description
一個星球上有 \(n\) 個國家和許多雙向道路,國家用 \(1\sim n\) 編號。
但是道路實在太多了,不能用通常的方法表示。於是我們以如下方式表示道路:\((a,b),(c,d)\) 表示,對於任意兩個國家 \(x,y\),如果 \(a\le x\le b,c\le y\le d\),那麼在 \(x,y\) 之間有一條道路。
首都位於 \(P\) 號國家。你想知道 \(P\) 號國家到任意一個國家最少需要經過幾條道路。保證 \(P\) 號國家能到任意一個國家。
\(1\le n\le 5\times 10^5\),\(1\le m\le 10^5\),\(1\le a\le b\le n\),\(1\le c\le d\le n\)。
Solution
這裡給出一個比較新穎的做法。
首先如果暴力建圖顯然會超時,而超時的原因是對於每個道路都兩兩建邊過於浪費,因為對於一個 \([a,b]\to[c,d]\) 的道路,只需要讓 \([a,b]\) 中 \(dis\) 最小的那個位置去更新 \([c,d]\)。
考慮類似 dijkstra 的過程,按 \(dis\) 從小到大確定每個 \(dis_x\),同時維護一個關於轉移邊的優先佇列。假如當前確定了 \(dis_x\),就暴力列舉所有還沒有更新過的道路 \([a,b]\to[c,d]\),將 \([c,d,dis_x+1]\) 加入優先佇列,表示可以讓 \([c,d]\) 這個區間裡的 \(dis\) 更新為 \(dis_x+1\)。
由於這裡是從小到大確定 \(dis\) 的,所以每次取出隊頭的轉移 \([l,r,v]\) 只需要暴力找到 \([l,r]\) 中還沒有確定 \(dis\) 的位置更新為 \(v\),同時維護優先佇列即可。
時間複雜度:\(O\left(\left(n+m\right)\log^2n\right)\)。
Code
#include <bits/stdc++.h>
#define int int64_t
const int kMaxN = 5e5 + 5;
int n, m, s;
int l1[kMaxN], r1[kMaxN], l2[kMaxN], r2[kMaxN], dis[kMaxN];
std::set<int> st, t[kMaxN * 4];
std::queue<std::tuple<int, int, int>> q;
void update(int x, int l, int r, int id, int op) {
int ql = l1[id], qr = r1[id];
if (l > qr || r < ql) {
return;
} else if (l >= ql && r <= qr) {
if (op == 1) t[x].emplace(id);
else t[x].erase(id);
return;
}
int mid = (l + r) >> 1;
update(x << 1, l, mid, id, op), update(x << 1 | 1, mid + 1, r, id, op);
}
void getvec(int x, int l, int r, int ql, std::vector<int> &vec) {
for (auto id : t[x]) vec.emplace_back(id);
if (l == r) return;
int mid = (l + r) >> 1;
if (ql <= mid) getvec(x << 1, l, mid, ql, vec);
else getvec(x << 1 | 1, mid + 1, r, ql, vec);
}
void upd(int x) {
std::vector<int> vec;
st.erase(x), getvec(1, 1, n, x, vec);
for (auto id : vec) {
update(1, 1, n, id, -1);
q.emplace(l2[id], r2[id], dis[x] + 1);
}
}
void dijkstra() {
for (int i = 1; i <= n; ++i) st.emplace(i);
for (int i = 1; i <= m; ++i) update(1, 1, n, i, 1);
memset(dis, 0x3f, sizeof(dis));
dis[s] = 0, upd(s);
for (; !q.empty();) {
auto [l, r, val] = q.front(); q.pop();
for (auto it = st.lower_bound(l); it != st.end() && *it <= r; it = st.lower_bound(l)) {
int u = *it;
dis[u] = val, upd(u);
}
}
}
void dickdreamer() {
std::cin >> n >> m >> s;
for (int i = 1; i <= m; ++i) {
std::cin >> l1[i] >> r1[i] >> l2[i] >> r2[i];
l1[i + m] = l2[i], r1[i + m] = r2[i], l2[i + m] = l1[i], r2[i + m] = r1[i];
}
m *= 2;
dijkstra();
for (int i = 1; i <= n; ++i) std::cout << dis[i] << '\n';
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}