【理论】长链剖分方法
我们学过重链剖分,它的划分方法是对于每个点 \(x\) 与其子树中大小最大的儿子 连边,由此形成若干条重链。
而长链剖分的划分和其大体上一致。
定义一个点的延伸深度为其子树内深度最大的点的深度。
则长链剖分的划分方法是对于每个点 \(x\) 与其子树中延伸深度最大的点连边,由此形成若干条长链。
长链的性质可以巧妙地适用于一些特殊的题目,本文仅介绍一个经典问题————树上 \(k\) 级祖先作为长链剖分性质和功能的初步展示。
树上 \(k\) 级祖先很显然有朴素倍增的做法,不过那样的时间复杂度为 \(O(n\log n)——O(\log n)\),单次询问的复杂度为 \(O(\log n)\),这个劣势在 \(n,q\) 同阶时表现得似乎并不明显,但当一道题具有 \(n=5\times10^5,q=2\times10^7\) 的数据范围是这可能是致命的 。
长链剖分以优化朴素倍增(依然基于倍增)的方式能够做到 \(O(n\log n)——O(1)\) 做到该问题。
考虑倍增之后的每次查询,我们本来要将 \(k\) 进行二进制分解,然后对于所有为 \(1\) 的位都跳一次。
但接下来我们转化一下思路,能不能别每位都跳一次?
设 \(k\) 的二进制最高位为 \(p\),那么我们只跳一次 \(2^p\) 级祖先,设跳到了 \(x'\),那么我们接下来的任务就是尝试 \(O(1)\) 求得 \(x'\) 的 \(k-2^p\) 级祖先。
自此,长链剖分的用处便显现了,由于二进制最高位的缘故,所以显然 \(2^p\ge k-2^p\),然后呢?我们发现由于 \(x'\) 已经是一个点的 \(2^k\) 级祖先了,那么其所在的长链的长度就必然 \(\ge2^k\),如果我们对每个长度为 \(len\) 的长链都往上预处理出链顶的 \(1\sim len\) 级祖先,可以发现,\(x'\) 所在的链本身加上该链预处理的向上的部分一定包括 \(x'\) 的\(k-2^p\) 级祖先,于是就做完了。
分析一下复杂度,单次询问显然 \(O(1)\),那么这个预处理的复杂度呢?
很显然每个点都在且仅在一个长链里面,预处理总复杂度为 \(\sum len=O(n)\)。
整体复杂度 \(O(n\log n)——O(1)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define inf 0x7fffffff
#define timeused() (double)clock()/CLOCKS_PER_SEC
#define rep(i,a,b) for(register int i=a,i##end=b;i<=i##end;++i)
#define repp(i,a,b) for(register int i=a,i##end=b;i>=i##end;--i)
#define mp make_pair
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
template<typename T> inline T rd(T& x){
T f=1;x=0;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(T)(c-'0');
x*=f;
return x;
}
#define ui unsigned int
ui s;
inline ui get(ui x) {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return s = x;
}
ll n,q,rt,dep[500005],mxdep[500005],son[500005],ld[500005],siz[500005],pos[500005],f[25][500005],Ans[5000005];
vector<ll> up[500005],down[500005],e[500005];
void dfs(ll x){
mxdep[x]=dep[x];
rep(i,0,e[x].size()-1){
ll to=e[x][i];
dep[to]=dep[x]+1;
dfs(to);
mxdep[x]=max(mxdep[x],mxdep[to]);
}
return;
}
void dfs2(ll x,ll Ld){
down[Ld].pb(x);
ld[x]=Ld;
pos[x]=siz[Ld]++;
rep(i,0,e[x].size()-1){
ll to=e[x][i];
if(!son[x]) son[x]=to;
else if(mxdep[to]>mxdep[son[x]]) son[x]=to;
}
if(!son[x]) return;
dfs2(son[x],Ld);
rep(i,0,e[x].size()-1){
ll to=e[x][i];
if(to!=son[x]) dfs2(e[x][i],e[x][i]);
}
return;
}
int main(){
rd(n);
rd(q);
rd(s);
rep(i,1,n){
rd(f[0][i]);
if(!f[0][i]) rt=i;
else e[f[0][i]].pb(i);
}
dep[rt]=1;
dfs(rt);
dfs2(rt,rt);
rep(i,1,n){
if(siz[i]){
ll now=i;
rep(j,0,siz[i]-1){
now=f[0][now];
up[i].pb(now);
}
}
}
rep(i,1,21) rep(j,1,n) f[i][j]=f[i-1][f[i-1][j]];
ll lstans=0;
rep(i,1,q){
Ans[i-1]=lstans;
ll x=(lstans^get(s))%n+1;
ll k=(lstans^get(s))%dep[x];
if(!k) lstans=x;
else{
ll qwq=log2(k);
x=f[qwq][x];
k-=1<<qwq;
if(dep[x]-dep[ld[x]]>=k) lstans=down[ld[x]][dep[x]-dep[ld[x]]-k];
else{
k-=dep[x]-dep[ld[x]];
lstans=up[ld[x]][k-1];
}
}
}
Ans[q]=lstans;
ll ans=0;
rep(i,1,q) ans^=i*Ans[i];
printf("%lld",ans);
}