码迷,mamicode.com
首页 > 其他好文 > 详细

长链剖分 解 k级祖先问题

时间:2020-04-28 09:26:55      阅读:56      评论:0      收藏:0      [点我收藏+]

标签:name   inline   spl   span   ||   概念   复杂   个数   时间   

概念

长链剖分, 和重链剖分一样是一种链剖分, 只不过重儿子变成了长儿子。

称一个点的子树深度(这个概念是我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(n\log 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级祖先问题

标签:name   inline   spl   span   ||   概念   复杂   个数   时间   

原文地址:https://www.cnblogs.com/tztqwq/p/12789673.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!