P6017题解
传送门:https://www.luogu.com.cn/problem/P6071
我们考虑 \([L,R]\) 所构成的子树的根节点 \(rt\),即 \([L,R]\) 的lca。这是个经典的问题答案为 \(\min\limits_{L\le i<R} lca_{i,i+1}\),用ST预处理查询。也等价于区间内dfn最小的两个点的lca,这种方法要两个ST表维护最大最小值,所以第一种实现比较方便点但是必须特判 \(L=R\) 的情况。
钦定 \(1\) 为整棵树的根分类讨论:
首先预处理任意点到 \(1\) 的距离 \(dis_{1,u}\) 和dfs序,深度。
-
\(p\) 在 \(rt\) 的子树外:我们达到 \(lca(p,rt)\) 后再走到 \(rt\) ,只能分叉去到不同的点,如果不分叉,那么lca的子节点也是 \([L,R]\) 的公共祖先,与 \(rt\) 是最近公共祖先相矛盾。答案即为 \(dis_{1,rt}+dis_{1,p}-2\times dis_{1,lca(p,rt)}\);
-
\(p\) 在 \(rt\) 的子树中:我们对于每个点最短路径为先向上走到 \(p\) 与其的lca处再往下走。我们找到 \([L,R]\) 中与 \(p\) 的lca中深度最高的点,记其为 \(k\)。走到最低点 \(k\) 后,要么存在某个点与 \(p\) 的lca在更高点,要么在 \(k\) 分叉即有两个不同的点与 \(p\) 的lca为 \(k\)。否则取 \(k\) 的某个子节点也能成为 \([L,R]\) 的公共祖先。特判掉 \(L=R\) 的情况,我们发现最后的路径即为 \(p\) 到与其深度最高的lca,也就是\(k\) 的路径。
长度即为 \(dis_{1,p}-dis_{1,k}\) 。如何求出 \(k\)?根据dfn求lca的原理,我们发现可能的 \(k\) 仅有可能是 \(p\) 与 \([L,R]\) 中dfn相较于 \(p\) 的dfn的前驱和后继的lca。二维平面信息差分,采用主席树维护值域即可。
总复杂度 \(O(n+q)\log_2n\) 。貌似是题解里分类讨论最少的?但是主席树常数非常大跑起来比较慢。
#include <bits/stdc++.h>
const int N = 2e6 + 1;
using namespace std;
struct Edge {
int v, next, w;
} e[N << 1];
int cnt, head[N];
void add(int u, int v, int w) {
e[cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt++;
}
int dep[N], fa[N][18], dis[N], dfn[N], dn, df[N];
void dfs(int x, int f, int v) {
dfn[x] = ++dn;
dep[x] = dep[f] + 1, fa[x][0] = f, dis[x] = dis[f] + v;
for (int i = 1; i <= 17; ++i) fa[x][i] = fa[fa[x][i - 1]][i - 1];
for (int i = head[x]; ~i; i = e[i].next) {
int y = e[i].v, w = e[i].w;
if (y == f) continue;
dfs(y, x, w);
}
}
int lca(int x, int y) {
if (dep[y] < dep[x]) swap(x, y);
for (int i = 17; ~i; --i) if (dep[fa[y][i]] >= dep[x]) y = fa[y][i];
if (y == x) return x;
for (int i = 17; ~i; --i) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
int root[N], ls[N * 18], rs[N * 18], tot, sz[N * 18];
void pushup(int p) { sz[p] = sz[ls[p]] + sz[rs[p]]; }
void update(int &p, int rt, int lp, int rp, int pos) {
p = ++tot;
if (lp == rp) {
sz[p] = sz[rt] + 1;
return;
}
int mid = (lp + rp) >> 1;
if (pos <= mid) rs[p] = rs[rt], update(ls[p], ls[rt], lp, mid, pos);
else ls[p] = ls[rt], update(rs[p], rs[rt], mid + 1, rp, pos);
pushup(p);
}
int pre(int L, int R, int lp, int rp, int l, int r) {
if (lp >= l && rp <= r) {
if (sz[R] - sz[L] == 0) return -1;
if (lp == rp) return lp;
}
int mid = (lp + rp) >> 1;
if (r > mid) {
int tmp = pre(rs[L], rs[R], mid + 1, rp, l, r);
if (~tmp) return tmp;
}
if (l <= mid) return pre(ls[L], ls[R], lp, mid, l, r);
return -1;
}
int suf(int L, int R, int lp, int rp, int l, int r) {
if (lp >= l && rp <= r) {
if (sz[R] - sz[L] == 0) return -1;
if (lp == rp) return lp;
}
int mid = (lp + rp) >> 1;
if (l <= mid) {
int tmp = suf(ls[L], ls[R], lp, mid, l, r);
if (~tmp) return tmp;
}
if (r > mid) return suf(rs[L], rs[R], mid + 1, rp, l, r);
return -1;
}
int Min[N][18], lg[N], n;
void work() {
for (int i = 1; i < n; ++i) Min[i][0] = lca(i, i + 1);
lg[2] = 1;
for (int i = 3; i < n; ++i) lg[i] = lg[i / 2] + 1;
for (int j = 1; j <= 17; ++j) {
for (int i = 1; i + (1 << j) - 1 < n; ++i) {
if (dep[Min[i][j - 1]] <= dep[Min[i + (1 << j - 1)][j - 1]]) Min[i][j] = Min[i][j - 1];
else Min[i][j] = Min[i + (1 << j - 1)][j - 1];
}
}
}
int query(int l, int r) {
int s = lg[r - l + 1];
if (dep[Min[l][s]] <= dep[Min[r - (1 << s) + 1][s]]) return Min[l][s];
return Min[r - (1 << s) + 1][s];
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
memset(head, -1, sizeof head);
int q;
cin >> n >> q;
for (int i = 1; i < n; ++i) {
int u, v, w;
cin >> u >> v >> w;
add(u, v, w), add(v, u, w);
}
dfs(1, 0, 0);
work();
for (int i = 1; i <= n; ++i) df[dfn[i]] = i;
for (int i = 1; i <= n; ++i) update(root[i], root[i - 1], 0, n + 1, dfn[i]);
int lastans = 0;
for (int i = 1; i <= q; ++i) {
int p, l, r;
cin >> p >> l >> r;
p ^= lastans, l ^= lastans, r ^= lastans;
if (l == r) {
cout << (lastans = dis[p] + dis[l] - 2 * dis[lca(p, l)]) << '\n';
continue;
}
int rt = query(l, r - 1);
if (lca(rt, p) != rt) {
cout << (lastans = dis[rt] + dis[p] - 2 * dis[lca(rt, p)]) << '\n';
continue;
}
int Pre = pre(root[l - 1], root[r], 0, n + 1, 1, dfn[p]), Suf = suf(
root[l - 1], root[r], 0, n + 1, dfn[p], n);
int k;
if (Pre == -1) k = df[Suf];
else if (Suf == -1) k = df[Pre];
else k = dep[lca(p, df[Pre])] >= dep[lca(p, df[Suf])] ? df[Pre] : df[Suf];
cout << (lastans = dis[p] - dis[lca(p, k)]) << '\n';
}
return 0;
}

树论,最近公共祖先,可持久化线段树
浙公网安备 33010602011771号