Luogu P6071 『MdOI R1』Treequery
Luogu P6071 『MdOI R1』Treequery
设节点 \(l\sim r\) 所构成的虚树为 \(T\). 容易发现, 答案即为子树 \(T\) 中离点 \(p\) 最近的点到 \(p\) 的距离.
于是将无根树强行变为有根树后, 分类讨论:
- 若 \(l\sim r\) 全是 \(p\) 的后代, 则答案为点 \(p\) 到它们 \(\text{LCA}\) 的距离 ;
- 若 \(l\sim r\) 部分是 \(p\) 的后代, 则答案为 \(0\) ;
- 若 \(l\sim r\) 全不是 \(p\) 的后代. 此时我们并不知道虚树上离 \(p\) 最近的点是哪个点, 于是还需分类讨论. 设 \(t=\operatorname{lca}\limits_{l\le x\le r}(x)\) .
- 若 \(t\) 不是 \(p\) 的祖先, 则 \(t\) 为子树 \(T\) 中离 \(p\) 最近的点. 答案此时为 \(t\) 到 \(p\) 的距离;
- 若 \(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
浙公网安备 33010602011771号