【洛谷5903】【模板】树上 k 级祖先(长链剖分)

点此看题面

大致题意: 给定一棵树,多组询问求(k)级祖先。

长链剖分

长链剖分板子题,通过(O(logn))的预处理,来(O(1))求出(k)级祖先。

具体做法

首先我们要知道一个性质:任意一个点(k)级祖先所在长链长度大于等于(k)

证明:假设长度小于(k),则从该祖先到该节点的链的长度就大于这条长链的长度,与长链的定义不符,故假设不成立,原命题得证。

接下来,我们一开始先通过倍增,求出(fa_{i,j})表示(i)(2^j)级祖先。

而与此同时,对于每条长链的顶点(t),我们依次用两个数组(u)(d)存下其(len(t))个祖先节点和长链上的所有节点(也是(len(t))个)。

然后,求答案时,设(k)的最高位为第(hb)位((HighestBit)),则我们先找到(x)(2^{hb})级祖先(f),并设(top_f)(f)所在长链的顶点。

根据先前证明的性质,(len(top_f)ge2^{hb})(2 imes 2^{hb})必然大于(k),所以(2 imes len(top_f))也必然大于(k),故可证明必然能在(top_f)(u)(d)数组中找到(x)(k)级祖先。

下一步,我们比较(dep_f-dep_{top_f})(k xor 2^{hb}),如果(dep_f-dep_{top_f}ge k xor 2^{hb}),说明最终答案在这条长链上,否则说明是顶点的祖先。

如果在这条长链上,返回(d_{top_f,(dep_f-dep_{top_f})-(k xor 2^{hb})}),否则返回(u_{top_f,(k xor 2^{hb})-(dep_f-dep_{top_f})})

具体实现中,由于每条长链只有链首需要开两个大小为链长的数组,因此所有(u)(d)完全可以存在一个数组中,只要记下每一个链首的数组在总数组中对应的位置(其实就是(dfs)序)即可查询。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
#define LN 20
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
#define U unsigned
using namespace std;
int n,rt,ee,lnk[N+5];U base;struct edge {int to,nxt;}e[N<<1];
I U Get(U x) {return x^=x<<13,x^=x>>17,x^=x<<5,base=x;}
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define D isdigit(c=tc())
		char c,*A,*B,FI[FS];
	public:
		I FastIO() {A=B=FI;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
		#undef D
}F;
class LongChainDissection
{
	private:
		int cnt,f[N+5][LN+5],g[N+5],D[N+5],len[N+5],tp[N+5],hb[N+5],p[N+5],u[N+5],d[N+5];
		I void dfs1(CI x)//第一遍dfs
		{
			RI i;for(i=1;i<=LN;++i) f[x][i]=f[f[x][i-1]][i-1];
			for(i=lnk[x];i;i=e[i].nxt) D[e[i].to]=D[f[e[i].to][0]=x]+1,
				dfs1(e[i].to),len[e[i].to]>len[g[x]]&&(g[x]=e[i].to);len[x]=len[g[x]]+1;//求出长儿子
		}
		I void dfs2(CI x,RI t,CI y=0)//第二遍dfs
		{
			RI i;p[x]=++cnt,d[p[x]]=x,u[p[x]]=y,tp[x]=t,g[x]&&(dfs2(g[x],t,f[y][0]),0);//求出u和d,先处理长儿子
			for(i=lnk[x];i;i=e[i].nxt) e[i].to^g[x]&&(dfs2(e[i].to,e[i].to,e[i].to),0);//处理其他儿子
		}
	public:
		I int operator [] (CI x) {return D[x];}I void Init()
		{
			D[rt]=1,dfs1(rt),dfs2(rt,rt);
			for(RI i=1,t=-1;i<=n;++i) (i>>t+1)&1&&++t,hb[i]=t;//预处理HighestBit
		}
		I int Anc(RI x,RI y)//求x的y级祖先
		{
			if(!y) return x;if(x=f[x][hb[y]],!(y^=1<<hb[y])) return x;//上跳
			RI t=D[x]-D[tp[x]];return t>=y?d[p[tp[x]]+t-y]:u[p[tp[x]]+y-t];//分二类讨论
		}
}D;
int main()
{
	RI Qt,i,x,y,lst=0;for(F.read(n,Qt,base),i=1;i<=n;++i) F.read(x),x?add(x,i):(rt=i);//读入
	long long ans=0;for(D.Init(),i=1;i<=Qt;++i) x=(Get(base)^lst)%n+1,//处理询问
		y=(Get(base)^lst)%D[x],ans^=1LL*i*(lst=D.Anc(x,y));return printf("%lld
",ans),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5903.html