Luogu P6071 『MdOI R1』Treequery

Luogu P6071 『MdOI R1』Treequery

设节点 \(l\sim r\) 所构成的虚树为 \(T\). 容易发现, 答案即为子树 \(T\) 中离点 \(p\) 最近的点到 \(p\) 的距离.

于是将无根树强行变为有根树后, 分类讨论:

  1. \(l\sim r\) 全是 \(p\) 的后代, 则答案为点 \(p\) 到它们 \(\text{LCA}\) 的距离 ;
  2. \(l\sim r\) 部分是 \(p\) 的后代, 则答案为 \(0\) ;
  3. \(l\sim r\) 全不是 \(p\) 的后代. 此时我们并不知道虚树上离 \(p\) 最近的点是哪个点, 于是还需分类讨论. 设 \(t=\operatorname{lca}\limits_{l\le x\le r}(x)\) .
    1. \(t\) 不是 \(p\) 的祖先, 则 \(t\) 为子树 \(T\) 中离 \(p\) 最近的点. 答案此时为 \(t\)\(p\) 的距离;
    2. \(t\)\(p\) 的祖先, 则子树 \(T\) 中离 \(p\) 最近的点为 \(l\sim r\) 中的点分别与 \(p\)\(\text{LCA}\) 中深度最大的点.

对上面情况的判断, 转换为 \(\text{dfn}\) 后就是一个静态二维数点的问题, 可以用主席树 \(\mathcal O(n\log n)\) . 一个区间内的所有点的 \(\text{LCA}\) 可以用 \(\text{ST}\) 表预处理出来. 现在的问题就是找到 \(l\sim r\) 中的点分别与 \(p\)\(\text{LCA}\) 中深度最大的点. 设其为 \(q\).

\(l\sim r\)\(\text{dfn}\) 序排序后, 把 \(p\) 点插进去. 由 \(\mathcal O(n\log n)-\mathcal O(1)\)\(\text{LCA}\) 的结论知, 若 \(p\) 的前驱为 \(p_1\), 则 \(p\)\(p_1\)\(\text{LCA}\) 是点 \(p\)\(p_1\) 之前点的 \(\text{LCA}\) 的后代 ; 后继也是同理. 于是点 \(q\) 必定是 \(p\) 与其前驱或后继的 \(\text{LCA}\).

考虑如何求出点 \(p\)\(l\sim r\) 中按 \(\text{dfn}\) 序的前驱/后继. 可以建立可持久化平衡树维护 \(\text{dfn}\) 序求前驱后继. 由于权值线段树也可以求前驱后继, 可用常数更小的主席树替代可持久化平衡树.

总时间复杂度: \(\mathcal O(n\log n)\)

参考代码
#include <bits/stdc++.h>
using namespace std;
static constexpr int Maxn = 2e5 + 5, LOG = 18;
int n, q, lastans, Lg2[Maxn * 4];
struct Edge { int to, w, nxt; } e[Maxn << 1];
int head[Maxn], etot;
void add_edge(int u, int v, int w) {
  e[++etot] = (Edge){v, w, head[u]}, head[u] = etot;
} // add_edge
#define enum_edge(u, i)                                     \
  for (int i = head[u]; i; i = e[i].nxt) if (e[i].to != fa)
int dfn[Maxn], idfn[Maxn], sz[Maxn], dfnt;
int dep[Maxn], wsum[Maxn], par[Maxn];
int edfn[Maxn * 2], iedfn[Maxn * 2], edfnt;
__attribute__((__always_inline__)) inline
bool compare_dfn_depth(int x, int y) { return dep[iedfn[x]] < dep[iedfn[y]]; }
void dfs(int u, int fa, int depth = 1) {
  par[u] = fa;
  idfn[dfn[u] = ++dfnt] = u;
  sz[u] = 1; dep[u] = depth;
  iedfn[edfn[u] = ++edfnt] = u;
  enum_edge(u, i) {
    int v = e[i].to, w = e[i].w;
    wsum[v] = wsum[u] + w;
    dfs(v, u, depth + 1); sz[u] += sz[v];
    iedfn[++edfnt] = u;
  }
} // dfs
int stLca[LOG + 1][Maxn * 2];
__attribute__((__always_inline__)) inline
int get_lca(int x, int y) {
  if (x == y) return x;
  int ex = edfn[x], ey = edfn[y];
  if (ex > ey) swap(ex, ey);
  int k = Lg2[ey - ex + 1];
  int ez = min(stLca[k][ex], stLca[k][ey - (1 << k) + 1], compare_dfn_depth);
  return iedfn[ez];
} // get_lca
int st2[LOG][Maxn];
__attribute__((__always_inline__)) inline
int ask_lca(int l, int r) {
  int k = Lg2[r - l + 1];
  return get_lca(st2[k][l], st2[k][r - (1 << k) + 1]);
} // ask_lca
struct treedot {
  int ls, rs;
  int s;
} tr[Maxn * LOG * 2];
int root[Maxn], ttot;
__attribute__((__always_inline__)) inline
int clone(int x) { tr[++ttot] = tr[x]; return ttot; }
__attribute__((__always_inline__)) inline
void pushup(int p) {
  tr[p].s = tr[tr[p].ls].s + tr[tr[p].rs].s;
} // pushup
void update(int &p, int l, int r, int x) {
  p = clone(p);
  if (l == r) {
    tr[p].s = 1;
  } else {
    int mid = (l + r) >> 1;
    if (x <= mid) update(tr[p].ls, l, mid, x);
    else update(tr[p].rs, mid + 1, r, x);
    pushup(p);
  }
} // update
int query(int pl, int pr, int l, int r, int L, int R) {
  if (!pr || tr[pr].s - tr[pl].s == 0) return 0;
  if (L == l && r == R) return tr[pr].s - tr[pl].s;
  int mid = (l + r) >> 1;
  if (R <= mid) return query(tr[pl].ls, tr[pr].ls, l, mid, L, R);
  if (L > mid) return query(tr[pl].rs, tr[pr].rs, mid + 1, r, L, R);
  return query(tr[pl].ls, tr[pr].ls, l, mid, L, mid) + query(tr[pl].rs, tr[pr].rs, mid + 1, r, mid + 1, R);
} // query
int find_rank(int pl, int pr, int l, int r, int x) {
  if (!pr || tr[pr].s - tr[pl].s == 0) return 0;
  if (l == r) return tr[pr].s - tr[pl].s;
  int mid = (l + r) >> 1;
  if (x <= mid) return find_rank(tr[pl].ls, tr[pr].ls, l, mid, x);
  else return (tr[tr[pr].ls].s - tr[tr[pl].ls].s) + find_rank(tr[pl].rs, tr[pr].rs, mid + 1, r, x);
} // find_rank
int get_rank(int pl, int pr, int l, int r, int x) {
  if (l == r) return l;
  int mid = (l + r) >> 1;
  int sls = tr[tr[pr].ls].s - tr[tr[pl].ls].s;
  if (x <= sls) return get_rank(tr[pl].ls, tr[pr].ls, l, mid, x);
  else return get_rank(tr[pl].rs, tr[pr].rs, mid + 1, r, x - sls);
} // get_rank
int main(void) {
  extern uint32_t readu32(void);
  n = readu32(), q = readu32();
  for (int i = 2; i < Maxn * 4; ++i) Lg2[i] = Lg2[i >> 1] + 1;
  for (int i = 1; i < n; ++i) {
    int u, v, w;
    u = readu32(), v = readu32(); w = readu32();
    add_edge(u, v, w);
    add_edge(v, u, w);
  }
  dfs(1, 0);
  for (int i = 1; i <= edfnt; ++i) stLca[0][i] = i;
  for (int j = 1; j < LOG + 1; ++j) for (int i = 1; i + (1 << j) - 1 <= edfnt; ++i)
    stLca[j][i] = min(stLca[j - 1][i], stLca[j - 1][i + (1 << j - 1)], compare_dfn_depth);
  for (int i = 1; i <= n; ++i) st2[0][i] = i;
  for (int j = 1; j < LOG; ++j) for (int i = 1; i + (1 << j) - 1 <= n; ++i)
    st2[j][i] = get_lca(st2[j - 1][i], st2[j - 1][i + (1 << j - 1)]);
  for (int i = 1; i <= n; ++i) update(root[i] = root[i - 1], 1, n, dfn[i]);
  for (lastans = 0; q--; ) {
    int l, r, x;
    x = readu32() ^ lastans, l = readu32() ^ lastans, r = readu32() ^ lastans;
    int inside = query(root[l - 1], root[r], 1, n, dfn[x], dfn[x] + sz[x] - 1);
    if (inside == r - l + 1) {
      int lca = ask_lca(l, r);
      lastans = wsum[lca] - wsum[x];
      printf("%d\n", lastans);
    } else if (inside == 0) {
      int lca = ask_lca(l, r);
      if (get_lca(lca, x) != lca) {
        int llca = get_lca(lca, x);
        lastans = (wsum[lca] - wsum[llca]) + (wsum[x] - wsum[llca]);
        printf("%d\n", lastans);
      } else {
        int rnk = find_rank(root[l - 1], root[r], 1, n, dfn[x]);
        int prv = (rnk == 0 ? 1 : get_rank(root[l - 1], root[r], 1, n, rnk));
        int nxt = (rnk == r - l + 1 ? 1 : get_rank(root[l - 1], root[r], 1, n, rnk + 1));
        prv = idfn[prv];
        nxt = idfn[nxt];
        int prv_lca = get_lca(prv, x);
        int nxt_lca = get_lca(nxt, x);
        int anc = (dep[prv_lca] > dep[nxt_lca] ? prv_lca : nxt_lca);
        lastans = wsum[x] - wsum[anc];
        printf("%d\n", lastans);
      }
    } else {
      printf("%d\n", lastans = 0);
    }
  }
  exit(EXIT_SUCCESS);
} // main
// fast io
static const int _BUF_SIZE = 1 << 18;
static char _ibuf[_BUF_SIZE], *iS = _ibuf, *iT = _ibuf;
inline char getch(void) {
  if (__builtin_expect(iS == iT, false))
    iT = (iS = _ibuf) + fread(_ibuf, 1, _BUF_SIZE, stdin);
  if (__builtin_expect(iS == iT, false)) return EOF;
  else return *iS++;
} // getch
uint32_t readu32(void) {
  register uint32_t x = 0;
  register char ch = getch();
  while (ch < '0' || ch > '9') ch = getch();
  while (ch >= '0' && ch <= '9') ((x += (x << 2)) <<= 1) += (ch ^ '0'), ch = getch();
  return x;
} // readu32
posted @ 2021-10-30 14:38  cutx64  阅读(51)  评论(0)    收藏  举报