把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷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\times 2^{hb}\)必然大于\(k\),所以\(2\times 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\n",ans),0;
}
posted @ 2020-07-17 20:02  TheLostWeak  阅读(553)  评论(0编辑  收藏  举报