长链剖分 解 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级祖先问题的主要内容,如果未能解决你的问题,请参考以下文章

长链剖分总结

长链剖分练习

Vijoslxhgww的奇思妙想

CF860EArkady and a Nobody-men 长链剖分

luogu P5384 [Cnoi2019]雪松果树

bzoj3252攻略(长链剖分+贪心)