P3242 接水果笔记

CTHOOH / 2024-03-07 / 原文

好题。

题意

给定一个 \(n\) 个结点的树,并且还给定了大小为 \(p\) 的一个带权路径集合 \(S\)\(S\) 中的路径 \((x, y, v)\) 表示这是一条端点为 \(x, y\) 且权值为 \(v\) 的路径。

\(q\) 个询问,每个询问给出 \((x, y), k\),查询 \(S\) 中所有被 \((x, y)\) 包含的路径中,权值第 \(k\) 小的路径。

\(n, p, q \le 4 \times 10^4\)

题解

考虑整体二分。

于是现在问题是:处理每个询问包含了几条给定路径。

考虑 \((x, y)\) 包含 \((u, v)\) 的条件,设 \(L_p, R_p\) 分别表示 \(p\) 的 dfn 序和 \(p\) 子树中最大的 dfn 序。

下面讨论 \((x, y)\)\((u, v)\) 的路径包含条件,钦定 \(L_x < L_y\)

  • \(LCA(u, v) = u\),那么令 \(k\)\(v \to u\) 的路径上倒数第二个点,则充要条件是 \((L_x < L_k \lor L_x > R_k) \land (L_y \in [L_v, R_v])\)
  • \(LCA(u, v) \neq u\),那么充要条件是 \((L_x \in [L_u, R_u]) \land L_y \in [L_v, R_v]\)

转化一下就可以变成矩形覆盖问题。

于是现在,一个询问 \((x, y)\) 所包含的路径数就是 \((L_x, L_y)\) 被多少个矩形包含,这个可以用 K-D Tree 做。

代码:

code:

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 4E4 + 5, LG = 21;
int n, p, tot, q, L[N], R[N], Ans[N], yf[N][LG], dep[N];
vector <int> G[N];
struct E {
  int typ, x, y, w, wtf;
  bool operator < (const E &l) const {return w < l.w;}
} e[N];
struct Q {
  int x, y, k, id;
} a[N], a1[N], a2[N];
void dfs(int x, int fa) {
  L[x] = ++tot;
  for (auto v : G[x]) {
    if (v == fa) continue;
    dep[v] = dep[x] + 1; yf[v][0] = x;
    for (int i = 1; i < LG; ++i)
      yf[v][i] = yf[yf[v][i - 1]][i - 1];
    dfs(v, x);
  } R[x] = tot;
}
int glca(int u, int v, bool cao = 0) {
  if (cao && u == v) return -1;
  if (dep[u] < dep[v]) swap(u, v);
  for (int i = LG - 1; i >= 0; --i)
    if (dep[u] - (1 << i) >= (cao ? dep[v] + 1 : dep[v])) u = yf[u][i];
  if (cao && yf[u][0] == v) return u;
  if (u == v) return u;
  for (int i = LG - 1; i >= 0; --i)
    if (yf[u][i] != yf[v][i]) u = yf[u][i], v = yf[v][i];
  return (cao ? u : yf[u][0]);
}
struct kdt {
  struct pos {int x[2], val;} po[N << 2], P;
  int b[N << 2], cnt, rt[LG], m;
  struct node {
    int ls, rs, sum;
    int L[2], R[2], x[2], val;
  } t[N << 2];
  void pushup(int x) {
    t[x].sum = t[t[x].ls].sum + t[t[x].rs].sum + t[x].val;
    for (int k : {0, 1}) {
      t[x].L[k] = t[x].R[k] = t[x].x[k];
      if (t[x].ls) {
        t[x].L[k] = min(t[x].L[k], t[t[x].ls].L[k]);
        t[x].R[k] = max(t[x].R[k], t[t[x].ls].R[k]);
      }
      if (t[x].rs) {
        t[x].L[k] = min(t[x].L[k], t[t[x].rs].L[k]);
        t[x].R[k] = max(t[x].R[k], t[t[x].rs].R[k]);
      }
    }
  }
  int build(int l, int r, int dep) {
    int mid = (l + r) >> 1;
    nth_element(b + l, b + mid, b + r + 1, [&](int x, int y) {return po[x].x[dep] < po[y].x[dep];});
    int x = b[mid]; t[x].x[0] = po[x].x[0];
    t[x].x[1] = po[x].x[1]; t[x].val = po[x].val;
    if (l < mid) t[x].ls = build(l, mid - 1, dep ^ 1);
    if (r > mid) t[x].rs = build(mid + 1, r, dep ^ 1);
    return pushup(x), x;
  }
  void append(int &x) {
    if (!x) return ; b[++cnt] = x;
    append(t[x].ls); append(t[x].rs);
    x = 0;
  }
  int query(int x) {
    if (!x) return 0;
    bool flg = 1;
    for (int k : {0, 1}) flg &= (t[x].R[k] <= P.x[k]);
    if (flg) return t[x].sum; 
    for (int k : {0, 1}) flg |= (t[x].L[k] > P.x[k]);
    if (flg) return 0; flg = 1;
    for (int k : {0, 1}) flg &= (t[x].x[k] <= P.x[k]);
    return query(t[x].ls) + query(t[x].rs) + flg * t[x].val;
  }
  void push(int x, int y, int val) {
    ++m; po[m].x[0] = x; po[m].x[1] = y;
    po[m].val = val; b[cnt = 1] = m;
    for (int i = 0; i < LG; ++i) {
      if (!rt[i]) {rt[i] = build(1, cnt, 0); break;}
      else append(rt[i]);
    }
  }
  void push(int lx, int rx, int ly, int ry) {
    if (rx < lx || ry < ly) return ;
    push(lx, ly, 1);
    push(lx, ry + 1, -1);
    push(rx + 1, ly, -1);
    push(rx + 1, ry + 1, 1);
  }
  int query(int x, int y) {
    P.x[0] = x, P.x[1] = y;
    int res = 0;
    for (int i = 0; i < LG; ++i) res += query(rt[i]);
    return res;
  }
  void clear() {
    for (int i = 0; i < LG; ++i) append(rt[i]);
    cnt = m = 0;
  }
} t;
void solve(int l, int r, int ql, int qr) {
  if (ql == qr) {
    for (int i = l; i <= r; ++i)
      Ans[a[i].id] = e[ql].w;
    return ;
  }
  int mid = (ql + qr) >> 1, cnt1 = 0, cnt2 = 0;  for (int i = ql; i <= mid; ++i) {
    int u = e[i].x, v = e[i].y;
    if (e[i].typ == 1) {
      t.push(1, L[u] - 1, L[v], R[v]);
      t.push(R[u] + 1, n, L[v], R[v]);
      t.push(L[v], R[v], 1, L[u] - 1);
      t.push(L[v], R[v], R[u] + 1, n);
    } else t.push(L[u], R[u], L[v], R[v]);
  }
  for (int i = l; i <= r; ++i) {
    int tmp = t.query(L[a[i].x], L[a[i].y]);
    if (tmp >= a[i].k) a1[++cnt1] = a[i];
    else a[i].k -= tmp, a2[++cnt2] = a[i];
  } t.clear();
  for (int i = 1; i <= cnt1; ++i)
    a[l + i - 1] = a1[i];
  for (int i = 1; i <= cnt2; ++i)
    a[l + cnt1 + i - 1] = a2[i];
  solve(l, l + cnt1 - 1, ql, mid);
  solve(l + cnt1, r, mid + 1, qr);
}
signed main(void) {
  ios :: sync_with_stdio(false);
  cin.tie(nullptr); cout.tie(nullptr);
  cin >> n >> p >> q;
  for (int i = 1; i < n; ++i) {
    int u, v; cin >> u >> v;
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  } dep[1] = 1;
  for (int i = 0; i < LG; ++i) yf[1][i] = 1;
  dfs(1, 0);
  for (int i = 1; i <= p; ++i) {
    cin >> e[i].x >> e[i].y >> e[i].w;
    if (L[e[i].x] > L[e[i].y]) swap(e[i].x, e[i].y);
    e[i].typ = 1 + (glca(e[i].x, e[i].y) != e[i].x);
    if (e[i].typ == 1) e[i].x = glca(e[i].x, e[i].y, 1);
  }
  sort(e + 1, e + 1 + p);
  for (int i = 1; i <= q; ++i) {
    cin >> a[i].x >> a[i].y >> a[i].k;
    a[i].id = i;
    if (L[a[i].x] > L[a[i].y]) swap(a[i].x, a[i].y);
  } solve(1, q, 1, p);
  for (int i = 1; i <= q; ++i) cout << Ans[i] << '\n';
}