【题目链接】
【思路要点】
- 对于区间 [ l , r ] [l,r] [l,r] ,考虑 [ l − 1 , l − 1 ] [l-1,l-1] [l−1,l−1] 对应的点 x x x 和 [ r + 1 , r + 1 ] [r+1,r+1] [r+1,r+1] 对应的点 y y y ,记 L c a Lca Lca 为 x , y x,y x,y 的最近公共祖先。
- 若 x x x 到 L c a Lca Lca 链上的某个点具有不在该链上的右儿子,则该右儿子会被定位到;若 y y y 到 L c a Lca Lca 链上的某个点具有不在该链上的左儿子,则该左儿子会被定位到,并且不难发现以上节点恰好就是被定位到的所有节点。
- 树上差分后按照 u u u 是否在 L c a Lca Lca 子树内分类讨论即可。
- 时间复杂度 O ( N L o g N + M L o g N ) O(NLogN+MLogN) O(NLogN+MLogN) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 4e5 + 5; const int MAXLOG = 20; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, tot, lc[MAXN], rc[MAXN], home[MAXN]; int root, depth[MAXN], father[MAXN][MAXLOG]; int build(int l, int r) { int pos = ++tot; if (l == r) { home[l] = pos; return pos; } int mid; read(mid); lc[pos] = build(l, mid); father[lc[pos]][0] = pos; rc[pos] = build(mid + 1, r); father[rc[pos]][0] = pos; return pos; } int lca(int x, int y) { if (depth[x] < depth[y]) swap(x, y); for (int i = MAXLOG - 1; i >= 0; i--) if (depth[father[x][i]] >= depth[y]) x = father[x][i]; if (x == y) return x; for (int i = MAXLOG - 1; i >= 0; i--) if (father[x][i] != father[y][i]) { x = father[x][i]; y = father[y][i]; } return father[x][0]; } int climb(int x, int y) { for (int i = MAXLOG - 1; i >= 0; i--) if (depth[father[x][i]] > depth[y]) x = father[x][i]; return x; } void dfs(int pos, int fa) { depth[pos] = depth[fa] + 1; for (int i = 1; i < MAXLOG; i++) father[pos][i] = father[father[pos][i - 1]][i - 1]; if (lc[pos]) dfs(lc[pos], pos); if (rc[pos]) dfs(rc[pos], pos); } pair <int, ll> lsum[MAXN], rsum[MAXN]; void work(int pos, int fa) { if (lc[pos]) { lsum[lc[pos]] = lsum[pos]; rsum[lc[pos]] = rsum[pos]; rsum[lc[pos]].first += 1; rsum[lc[pos]].second += depth[rc[pos]]; work(lc[pos], pos); } if (rc[pos]) { lsum[rc[pos]] = lsum[pos]; rsum[rc[pos]] = rsum[pos]; lsum[rc[pos]].first += 1; lsum[rc[pos]].second += depth[lc[pos]]; work(rc[pos], pos); } } int main() { read(n), build(1, n); home[0] = ++tot; lc[tot + 1] = tot; father[tot][0] = tot + 1; rc[tot + 1] = 1; father[1][0] = ++tot; home[n + 1] = ++tot; lc[tot + 1] = tot - 1; father[tot - 1][0] = tot + 1; rc[tot + 1] = tot; father[tot][0] = tot + 1; dfs(root = ++tot, 0); work(root, 0); read(m); for (int i = 1; i <= m; i++) { int u, l, r; read(u), read(l), read(r); l = home[l - 1], r = home[r + 1]; int x = lca(l, r), tl = climb(l, x), tr = climb(r, x); ll ans = rsum[l].second - rsum[tl].second + lsum[r].second - lsum[tr].second; int cnt = rsum[l].first - rsum[tl].first + lsum[r].first - lsum[tr].first; ans += 1ll * cnt * depth[u]; if (u == x || lca(u, x) != x) { int y = lca(u, x); ans -= 2ll * cnt * depth[y]; } else if (lca(u, l) != x) { int y = lca(u, l); ans -= 2ll * (lsum[r].first - lsum[tr].first) * depth[x]; ans -= 2ll * (rsum[l].first - rsum[y].first) * depth[y]; ans -= 2ll * (rsum[y].second - rsum[tl].second - (rsum[y].first - rsum[tl].first)); if (y != u && climb(u, y) == rc[y]) ans -= 2; } else { int y = lca(u, r); ans -= 2ll * (rsum[l].first - rsum[tl].first) * depth[x]; ans -= 2ll * (lsum[r].first - lsum[y].first) * depth[y]; ans -= 2ll * (lsum[y].second - lsum[tr].second - (lsum[y].first - lsum[tr].first)); if (y != u && climb(u, y) == lc[y]) ans -= 2; } writeln(ans); } return 0; }