P6348 [PA2011] Journeys 題解

下蛋爷發表於2024-08-23

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;
}