Loading

【理论】长链剖分方法

我们学过重链剖分,它的划分方法是对于每个点 \(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);
}
posted @ 2023-03-08 15:37  lstqwq  阅读(35)  评论(0编辑  收藏  举报