ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

长链剖分学习笔记

2022-07-03 14:32:07  阅读:72  来源: 互联网

标签:长链 剖分 int 笔记 祖先 uint 节点


概念

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

实现

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

有啥用?

它有这么些性质

  • 长链剖分后,所有节点都仅属于一条链(显然
  • 任意节点 \(u\) 的第 \(k\) 级祖先 \(f\) 所在链的长度一定大于 \(k\)(若 \(f,u\) 在一条链上,那么显然链长大于 \(k\) ;若 \(f,u\) 不在一条链上,假设 \(f\) 所在链长度小于 \(k\) ,那么 \(f-u\) 的链长显然更长,\(f,u\) 应在一条链上,矛盾)
  • 任意节点到达根节点经过的长链数是 \(\sqrt{n}\) 级的(由上条可得每次跳跃到的新链长度不会小于当前链,最坏的情况,长链长度为 \(1,2,3,…, \sqrt{n}\)单调递增)

应用

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有