长链剖分 解 k级祖先问题
Posted tztqwq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了长链剖分 解 k级祖先问题相关的知识,希望对你有一定的参考价值。
概念
长链剖分, 和重链剖分一样是一种链剖分, 只不过重儿子变成了长儿子。
称一个点的子树深度(这个概念是我yy的,在其他地方不一定适用)为:以这个点为根的子树中所有节点深度的最大值。
一个点的长儿子就是这个点的儿子中子树深度最大的那个儿子。
常见性质
1.对树长链剖分后, 所有链的 (size) ( (length) ) 之和为树的节点个数。
显然成立
2.一个点的 (k) 级祖先所在链的长度 (geq k)
若这个点的 (k) 祖先与其在同一链中, 则性质成立。
若反之,则可以推出这个点的 (k) 祖先的儿子们的最大子树深度大于 (k), 性质成立。
算法流程
前置定义
定义 (highbit(n)) 为 (log_2(n)) 的整数部分。
假设要求节点 (x) 的 (k) 级祖先。
记 (r = 2^{highbit(k)})。
真 · 流程
求出 (x) 的 (r) 级祖先 (y), 问题转化为求 (y) 的 (k-r) 级祖先。(由于 (r) 为 (2) 的整次幂, 所以可以倍增处理后 (O(1)) 回答)
由二进制的性质, 可知 (k-r<r) 。
由 常见性质2, (y) 所在链的长度 (geq r)。
故预处理出每个链 (L) 链顶的 (1 leq k leq length(L)) 级祖先和(1 leq k leq length(L)) 级儿子, 可以保证 (y) 的 (k-r) 级祖先必被包含其中。
时间复杂度分析
倍增预处理 (O(nlog n))。
预处理每个链 (L) 的 (length(L)) 个祖先和儿子, 复杂度是 (O(sum_{L} length(L)) = O(n))。
显而易见, 经过预处理之后, 对某个 (k) 级祖先问题的回答是可以做到 (O(1)) 的。
板子题
AC代码
// 注意, 以一个点 x 为顶端的链的长度为 treedep[x]-dep[x]+1;
// 由于这个 sb 错误我调了很久qwq
// 希望看这篇博文的人引以为戒 qwq
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5+15;
#define ui unsigned int
ui s;
inline ui get(ui x) {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return s = x;
}
int n,q,rt;
vector<int>e[maxn],ks[maxn],kf[maxn];
int treedep[maxn],dep[maxn],son[maxn],top[maxn];
int f[21][maxn];
void po1(int x,int fa,int Dep) {
treedep[x]=dep[x]=Dep;
f[0][x]=fa;
for(int i=0;i<(int)e[x].size();++i) {
int y=e[x][i]; if(y==fa) continue;
po1(y,x,Dep+1);
if(treedep[y]>treedep[son[x]]) treedep[x]=treedep[son[x]=y];
}
}
void po2(int x,int tp) {
top[x]=tp;
if(son[x]) po2(son[x],tp);
for(int i=0;i<(int)e[x].size();++i) {
int y=e[x][i]; if(y==f[0][x]||y==son[x]) continue;
po2(y,y);
}
}
int highbit[maxn];
int kfa(int x,int k) {
if(!k) return x;
int r=highbit[k];
x=f[r][x];
k-=(1<<r);
if(dep[x]-k<dep[top[x]])
return kf[top[x]][dep[top[x]]-(dep[x]-k)];
else
return ks[top[x]][(dep[x]-k)-dep[top[x]]];
}
int ans[5000005];
int main()
{
cin>>n>>q>>s;
for(int i=1;i<=n;++i) {
int ff;scanf("%d",&ff);
if(ff) e[ff].push_back(i);
else rt=i;
}
po1(rt,0,1);
po2(rt,rt);
for(int k=1;k<=20;++k)
for(int i=1;i<=n;++i)
f[k][i] = f[k-1][f[k-1][i]];
for(int x=1;x<=n;++x) if(x==top[x]) {
int ns=x, nf=x;
for(int i=0;i<=treedep[x]-dep[x]+1;++i) {
ks[x].push_back(ns), kf[x].push_back(nf);
ns=son[ns], nf=f[0][nf];
}
}
for(int i=1;i<=n;++i) highbit[i] = log2(i);
for(int i=1;i<=q;++i) {
int x=((get(s) xor ans[i-1]) % n)+1;
int k=(get(s) xor ans[i-1])%dep[x];
ans[i] = kfa(x,k);
}
long long ANS = 0ll;
for(int i=1;i<=q;++i) ANS ^= 1ll*i*ans[i];
cout << ANS;
return 0;
}
以上是关于长链剖分 解 k级祖先问题的主要内容,如果未能解决你的问题,请参考以下文章