P3242 接水果笔记

好题。

题意

给定一个 \(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';
}
posted @ 2024-03-06 23:52  CTHOOH  阅读(38)  评论(0)    收藏  举报