P10304 [THUWC 2020] 道路修建

CJzdc發表於2024-11-19

注意到 \(1\) 到一個 \(b\) 子樹內的點 \(x\) 的路徑可以拆成 \(1\to p\to q\to x\) 的形式,其中 \(1\to p\) 走樹邊,\(p\to q\) 為在點 \(p\) 從樹邊走出去,在點 \(q\) 走回來,然後 \(q\) 再走樹邊走到 \(x\)

考慮 \(f_i\) 為最小的 \(d\),滿足斷掉 \(i\) 深度為 \(d\) 的祖先到 \(i\) 的鏈後,\(1\) 仍然能到 \(i\)。那麼一個點 \(x\) 能到達當且僅當存在 \(q\)\(b\to x\) 路徑上,且 \(f_q\le dep_a\),即 \(\min\limits_{q\in path(b,x)}f_q\le dep_a\)

考慮怎麼求 \(f\)。這個可以按照拓撲序從小往大列舉 \(u\),同時處理所有非樹邊 \(u\to v\),貢獻為 \(u\)\(\operatorname{LCA}(u, v)\) 這條鏈上 \(f\) 的最大值。因為此時 \(u\to \operatorname{LCA}(u, v)\) 鏈上的 \(f\) 都已知,可以直接倍增。

現在我們已知了 \(f\),把每組詢問掛在 \(b\) 上,動態維護 \(\min f\) 的權值線段樹,不難線段樹合併做到 \(O((n+m+q)\log n)\)

code
#include <bits/stdc++.h>
#define ALL(x) begin(x), end(x)
using namespace std;
void file() {
  freopen("1.in", "r", stdin);
  freopen("1.out", "w", stdout);
}
using ll = long long;
using PII = pair<int, int> ;

const int kInf = 1e9;
const int kN = 1.5e5 + 5;

int n, m, q;
array<int, kN> fa, ord, in, dep, f, ans;
array<bool, kN> intr, vis;
array<PII, kN> ed;
array<vector<PII>, kN> g, qry;
array<vector<int>, kN> tr, buc;
array<array<int, kN>, 20> jp, mn;

void toposort() {
  for(int i = 1; i <= m; i++)
    in[ed[i].second]++;
  queue<int> q;
  for(int i = 1; i <= n; i++)
    if(!in[i]) q.push(i);
  int tot = 0;
  while(q.size()) {
    int x = q.front(); q.pop();
    ord[++tot] = x;
    for(auto k : g[x]) {
      int to = k.first;
      if(!--in[to]) q.push(to);
    }
  }
}

void build() {
  for(int i = 1; i <= n; i++)
    sort(ALL(g[i]), greater<> ());
  dep[0] = -1;
  for(int i = 1, x = 1; i <= n; i++) {
    vis[x] = 1;
    for(bool flag = 0; x; x = fa[x]) {
      while(g[x].size()) {
        auto k = g[x].back(); g[x].pop_back();
        int to = k.first;
        if(vis[to]) continue;
        else {
          intr[k.second] = flag = 1;
          f[to] = dep[to] = dep[x] + 1, fa[to] = x;
          tr[x].push_back(to), x = to;
          break;
        }
      }
      if(flag) break;
    }
  }
}

void init(int x) {
  mn[0][x] = f[x];
  for(int i = 1; i < 19; i++)
    mn[i][x] = min(mn[i - 1][x], mn[i - 1][jp[i - 1][x]]);
}
int lca(int x, int y) {
  if(dep[x] < dep[y]) swap(x, y);
  for(int i = 19; ~i; i--)
    if(dep[jp[i][x]] >= dep[y]) x = jp[i][x];
  if(x == y) return x;
  for(int i = 19; ~i; i--)
    if(jp[i][x] ^ jp[i][y])
      x = jp[i][x], y = jp[i][y];
  return fa[x];
}
int query(int anc, int x) {
  int ans = f[anc];
  for(int i = 19; ~i; i--)
    if(dep[jp[i][x]] >= dep[anc])
      ans = min(ans, mn[i][x]), x = jp[i][x];
  return ans;
}

const int kS = 5e6 + 5;

struct SGT {
  int tot = 0;
  array<int, kN> root;
  array<int, kS> ls, rs, sum;

  void pu(int o) { sum[o] = sum[ls[o]] + sum[rs[o]]; }
  void modify(int& o, int l, int r, int x, int v) {
    if(!o) o = ++tot;
    sum[o] += v;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    if(mid < x) modify(rs[o], mid + 1, r, x, v);
    else modify(ls[o], l, mid, x, v);
  }
  void erase(int& o, int l, int r, int x, int y) {
    if((l > y) || (r < x)) return ;
    if((l >= x) && (r <= y)) return void(o = 0);
    int mid = (l + r) >> 1;
    erase(ls[o], l, mid, x, y);
    erase(rs[o], mid + 1, r, x, y);
    pu(o);
  }
  int query(int o, int l, int r, int x, int y) {
    if((l > y) || (r < x)) return 0;
    if((l >= x) && (r <= y)) return sum[o];
    int mid = (l + r) >> 1;
    return query(ls[o], l, mid, x, y) + query(rs[o], mid + 1, r, x, y);
  }
  int merge(int x, int y, int l, int r) {
    if(!x || !y) return x + y;
    sum[x] += sum[y];
    if(l == r) return x;
    int mid = (l + r) >> 1;
    ls[x] = merge(ls[x], ls[y], l, mid);
    rs[x] = merge(rs[x], rs[y], mid + 1, r);
    return x;
  }
}sgt;

void dfs(int x) {
  for(int to : tr[x])
    dfs(to), sgt.root[x] = sgt.merge(sgt.root[x], sgt.root[to], 0, n);
  int sum = sgt.query(sgt.root[x], 0, n, f[x] + 1, n) + 1;
  sgt.erase(sgt.root[x], 0, n, f[x] + 1, n);
  sgt.modify(sgt.root[x], 0, n, f[x], sum);
  for(auto k : qry[x]) {
    int dep = k.first, id = k.second;
    ans[id] = sgt.query(sgt.root[x], 0, n, dep + 1, n);
  }
}

int main() {
  // file();
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m >> q;
  for(int i = 1, u, v; i <= m; i++) {
    cin >> u >> v;
    ed[i] = PII {u, v};
    g[u].emplace_back(v, i);
  }
  toposort(), build();
  for(int i = 1; i <= m; i++) {
    int u = ed[i].first, v = ed[i].second;
    if(!intr[i]) buc[u].push_back(v);
  }
  jp[0] = fa;
  for(int i = 1; i < 20; i++)
    for(int j = 1; j <= n; j++)
      jp[i][j] = jp[i - 1][jp[i - 1][j]];
  for(auto& k : mn) k.fill(kInf);
  for(int i = 1; i <= n; i++) {
    int x = ord[i]; init(x);
    for(int to : buc[x]) {
      int anc = lca(x, to);
      f[to] = min(f[to], query(anc, x));
    }
  }
  for(int i = 1, a, b; i <= q; i++) {
    cin >> a >> b;
    qry[b].emplace_back(dep[a], i);
  }
  dfs(1);
  for(int i = 1; i <= q; i++)
    cout << ans[i] << "\n";
  return 0;
}

相關文章