LGP5903 [LG TPLT] 树上 K 级祖先 学习笔记

LGP5903 [LG TPLT] 树上 K 级祖先 学习笔记

Luogu Link

题意简述

给定一棵 \(n\) 个点的有根树。\(q\) 次询问,每次给定 \(u_i,k_i\),求 \(u_i\)\(k_i\) 级祖先。强制在线。

\(n\le 5\times 10^5,q\le 5\times 10^6\)

做法解析

借助长链剖分,我们可以 \(O(n\log n)\sim O(1)\) 在线解决这个问题。

长剖是什么?长剖是树链剖分的一种,它将树剖为若干条链,对于任意点 \(u\),我们让它的长儿子(子树内最深结点深度最深的儿子)和父亲共一条链,让其它儿子分家。

我们怎么利用长剖做这个问题呢?首先我们先做一些预处理,除了长剖外,我们用树上倍增求出每个点的 \(2^x\) 级祖先(这一步复杂度 \(O(n\log n)\),也是该做法时间复杂度瓶颈),预处理 \(i\in [1,n]\) 在二进制下的最高位序号记为 \(h_i\)。对于长剖出来的每条长链,设其长度为 \(len\),则记录该长链顶点直链往上往下 \(len\) 个结点都是什么(这一步 \(O(n)\),因为所有长链长度之和为 \(n\))。

对于每次询问,我们先跳到 \(u\)\(2^{h_k}\) 级祖先 \(a\),设剩下还有 \(d\) 级。这里有一个性质:\(a\) 所在的长链从 \(a\) 往下的长度一定不小于 \(2^{h_k}\)(毕竟 \(u\) 就是跳了这么多格上来的,\(a\) 子树内最深的点一定不比 \(u\) 浅),所以这长度一定也大于 \(d\)。接着设我们一步跨了 \(t\) 层到了长链的顶点。那么最终的答案就是长链顶点往上 \(d-t\) 步(\(d-t\) 是负数代表往下)的答案,我们之前预处理过了。

你确定预处理过了?怎么证明跳 \(d-t\) 步不会跳出长剖的处理范围?确定。
如果往上跳 \(d-t\) 步跳出了长剖处理范围,说明 \(k>2^{h_k}+len\ge 2^{h_k}+(2^{h_k}+t)\),说明 \(k\ge 2^{h_{k+1}}\),说明 \(h_k\) 不是 \(k\) 最高位,矛盾。
如果往下跳 \(t-d\) 步跳出了长剖处理范围,说明跳到了不比 \(u\) 浅的地方,不可能。

所以这个东西我们一定已经预处理出来了,直接查就行了,由此单次询问复杂度 \(O(1)\)

代码实现

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=5e5+5,MaxNb=21;
int N,Q,X,Y;uint S;
//ProbPresetBegin
uint aget(uint x) {
    x^=x<<13,x^=x>>17,x^=x<<5;
    S=x;return x;
}
//ProbPresetEnd
vector<int> Tr[MaxN];
int rt,tfa[MaxN][MaxNb],dep[MaxN],epd[MaxN],dson[MaxN];
void dfs1(int u){
    epd[u]=dep[u]=dep[tfa[u][0]]+1;
    for(int v : Tr[u]){
        tfa[v][0]=u;
        for(int i=0;tfa[v][i];i++)tfa[v][i+1]=tfa[tfa[v][i]][i];
        dfs1(v);if(epd[v]>epd[u])epd[u]=epd[v],dson[u]=v;
    }
}
int h[MaxN],top[MaxN];vector<int> upr[MaxN],lwr[MaxN];
void dfs2(int u,int t){
    top[u]=t;
    if(u==t){
        for(int i=0,c=u;i<=epd[u]-dep[u];i++,c=tfa[c][0])upr[u].push_back(c);
        for(int i=0,c=u;i<=epd[u]-dep[u];i++,c=dson[c])lwr[u].push_back(c);
    }
    if(dson[u])dfs2(dson[u],t);
    for(int v : Tr[u])if(v!=dson[u])dfs2(v,v);
}
lolo cans,fans;
int solve(int u,int k){
    if(!k)return u;
    u=tfa[u][h[k]],k-=(1<<h[k]);
    k-=dep[u]-dep[top[u]],u=top[u];
    return k>=0?upr[u][k]:lwr[u][-k];
}
int main(){
    readis(N,Q,S);
    h[0]=-1;for(int i=1;i<=N;i++)h[i]=h[i>>1]+1;
    for(int i=1;i<=N;i++)readi(X),Tr[X].push_back(i);
    rt=Tr[0][0],dfs1(rt),dfs2(rt,rt);
    for(int i=1;i<=Q;i++){
        X=(aget(S)^cans)%N+1;
        Y=(aget(S)^cans)%dep[X];
        cans=solve(X,Y),fans^=(1ll*i*cans);
    }
    writi(fans);
    return 0;
}
posted @ 2025-05-03 12:10  矞龙OrinLoong  阅读(6)  评论(0)    收藏  举报