其他分享
首页 > 其他分享> > 长链剖分学习笔记

长链剖分学习笔记

作者:互联网

概念

长链剖分属于链剖分的一种
一般讲的树剖指重链剖分,它可以用于维护树上路径的信息
而长链剖分则是用于维护有关深度的信息

实现

长链剖分的剖分方法与树链剖分极其相似,只需要把以子树大小判断重儿子改成以节点深度判断长儿子即可

有啥用?

它有这么些性质

应用

P5903 【模板】树上 k 级祖先

用树上倍增解决的话可以 \(O(n \log n)\) 预处理, \(O(\log n)\) 回答。但是不够快。长链剖分可以 \(O(n \log n)\) 预处理, \(O(1)\) 回答

预处理:

  1. 对树进行长链剖分,记录每条链的顶点和深度
  2. 树上倍增求出每个点的 \(2^n\) 级祖先
  3. 对于每条链,如果其长度为 \(len\) ,那么在顶点处记录顶点向上的 \(len\) 个祖先和向下的 \(len\) 个链上的儿子
  4. 对 \(i \in [1,n]\) 求出在二进制下的最高位 \(h_i\)

询问:利用倍增数组先将 \(x\) 跳到 \(x\) 的 \(2^{h_k}\) 级祖先,设剩下还有 \(k^{\prime}\) 级,显然 \(k^{\prime} < 2^{h_k}\) ,因此此时 \(x\) 所在的长链长度一定 \(\geq 2^{h_k} > k^{\prime}\) ,因此可以先将 \(x\) 跳到 \(x\) 所在链的顶点,若之后剩下的级数为正,则利用向上的数组求出答案,否则利用向下的数组求出答案。

Code:

#include <cstdio>
#include <vector>
typedef long long ll;
typedef unsigned int uint;
using namespace std;
const int N=5e5+7,LOGN=21;

vector<int> edge[N],up[N],down[N];

int dp[N][LOGN]; // dp[i][j]表示i的2^j级祖先
int LOG[N],d[N],dep[N],mxdep[N],son[N],top[N];

ll Ans;
uint s;
int n,q,root;

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

inline void dfs1(int x) {
	dep[x]=mxdep[x]=dep[dp[x][0]]+1;
	for(int i=0,y;i<edge[x].size();++i) {
		y=edge[x][i];
		dp[y][0]=x;
		for(int j=0;dp[y][j];++j)
			dp[y][j+1]=dp[dp[y][j]][j];
		dfs1(y);
		if(mxdep[y]>mxdep[x])
			mxdep[x]=mxdep[y],son[x]=y;
	}
}

inline void dfs2(int x,int topf) {
	top[x]=topf;
	if(x==topf) {
		for(int i=0,o=x;i<=mxdep[x]-dep[x];++i)
			up[x].push_back(o),o=dp[o][0];
		for(int i=0,o=x;i<=mxdep[x]-dep[x];++i)
			down[x].push_back(o),o=son[o];
	}
	if(son[x])
		dfs2(son[x],topf);
	for(int i=0,y;i<edge[x].size();++i) {
		y=edge[x][i];
		if(y!=son[x])
			dfs2(y,y);
	}
}

inline int query(int x,int k) {
	if(!k)
		return x;
	x=dp[x][LOG[k]]; // 将x跳到2^h[k]级祖先
	k-=1<<LOG[k],k-=dep[x]-dep[top[x]],x=top[x]; // 跳到所在链顶点
	return k>=0 ? up[x][k] : down[x][-k]; // 利用向上/向下数组求出答案
}

signed main() {
	scanf("%d%d%d",&n,&q,&s);
	LOG[0]=-1;
	for(int i=1;i<=n;++i)
		LOG[i]=LOG[i>>1]+1;
	for(int i=1;i<=n;++i) {
		scanf("%d",&dp[i][0]);
		edge[dp[i][0]].push_back(i);
	}
	root=edge[0][0];
	dfs1(root),dfs2(root,root);
	for(int i=1,x,k,lstans=0;i<=q;++i) {
		x=(get(s)^lstans)%n+1;
		k=(get(s)^lstans)%dep[x];
		lstans=query(x,k);
		Ans^=1ll*i*lstans;
	}
	printf("%lld",Ans);
    return 0;
}

标签:长链,剖分,int,笔记,祖先,uint,节点
来源: https://www.cnblogs.com/wshcl/p/clpf.html