树上 K 级祖先

基于重链剖分可以在 \(O(\log n)\) 的复杂度内求出树上一个点的 \(K\) 级祖先。

\(u\)\(K\) 级祖先:定义 \(u\) 的父节点为 \(u\)\(1\) 级祖先,记作 \(fa(u,1)\);再递归定义 \(fa(u,i)=fa(fa(u,i-1),1)\),那么 \(fa(u,K)\) 被称为 \(u\)\(K\) 级祖先

具体而言,重链剖分完后,我们在重链上跳。如果当前当前节点与链顶的距离小于等于 \(K\),那就跳到更上一条重链,同时将 \(K\) 减去该距离;否则说明 \(K\) 级祖先在当前重链上,那么该重链上编号比当前节点小 \(K\) 的点即为所求。整个过程基于重链剖分后单条重链上的节点编号连续的性质。

模板题:P5903 【模板】树上 K 级祖先

什么你说正解是长链剖分?不管了反正重链剖分似乎跑的更快呢。

#include<bits/stdc++.h>

using namespace std;

const int N = 5e5 + 10;

int n, q;
long long lastans, ans;

unsigned int s;

inline unsigned int get(unsigned int x)
{
	x ^= x << 13;
	x ^= x >> 17;
	x ^= x << 5;
	
	return s = x;
}

vector<int> e[N];
int root;

int dep[N], fa[N], siz[N], hs[N];
int top[N], id[N], ID[N], cnt;

void dfs1(int u, int father)
{
	dep[u] = dep[father] + 1;
	fa[u] = father;
	siz[u] = 1;
	
	int maxson = -1;
	
	for(auto i : e[u])
	{
		if(i == father)
		{
			continue;
		}
		
		dfs1(i, u);
		
		siz[u] += siz[i];
		
		if(maxson < siz[i])
		{
			maxson = siz[i];
			hs[u] = i;
		}
	}
}

void dfs2(int u, int topf)
{
	top[u] = topf;
	id[u] = ++ cnt;
	ID[cnt] = u;
	
	if(hs[u] == 0)
	{
		return;
	}
	
	dfs2(hs[u], topf);
	
	for(auto i : e[u])
	{
		if(i == fa[u] || i == hs[u])
		{
			continue;
		}
		
		dfs2(i, i);
	}
}

int faK(int x, int K)//K级祖先 
{
	while(K >= id[x] - id[top[x]] + 1 && x != root)
	{
		K -= (id[x] - id[top[x]] + 1);
		x = fa[top[x]];
	}
	
	return ID[id[x] - K];
}

int main()
{
	cin >> n >> q >> s;
	
	for(int i = 1; i <= n; i ++)
	{
		scanf("%d", &fa[i]);
		
		if(fa[i] == 0)
		{
			root = i;
			continue;
		}
		
		e[i].push_back(fa[i]);
		e[fa[i]].push_back(i);
	}
	
	dfs1(root, 0);
	dfs2(root, root);
	
	for(int i = 1; i <= q; i ++)
	{
		int x = (get(s) ^ lastans) % n + 1;
		int K = (get(s) ^ lastans) % dep[x];
		
		lastans = faK(x, K);
		ans ^= (i * lastans);
	}
	
	cout << ans;
	
	return 0;
}
posted @ 2025-10-30 11:24  cold_jelly  阅读(10)  评论(0)    收藏  举报