LGP5903 [LG TPLT] 树上 K 级祖先 学习笔记
LGP5903 [LG TPLT] 树上 K 级祖先 学习笔记
题意简述
给定一棵 \(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;
}
浙公网安备 33010602011771号