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;
}
posted @ 2026-02-22 16:08  Jefferyzzzz  阅读(15)  评论(0)    收藏  举报