当前位置: 首页 > article >正文

[学习笔记]树链剖分(简易版) 及其LCA

树链剖分

先讲解一下一些基础定义(都是在树上)

  • 重儿子: 一个节点中所有儿子中子树大小最大的一个儿子(每个节点最多有一个重儿子)
  • 轻儿子: 一个节点除重儿子外所有的节点
  • 重链: 若干个重儿子组成的链
  • 链顶: 一条链中深度最小的节点

以下图为例子 (红色连续线段为重链)
红色连续线段为重链
对于节点 1 1 1 来说, 子树大小最大的儿子是 2 2 2 , 2 2 2 最大的是 5 5 5 , 5 5 5的所有儿子子树大小一样大, 随机选一个即可
对于节点 3 3 3 来说, 虽然本身是一个轻儿子, 但是它有重儿子 4 4 4 , 所以也能构成一条重链
由此观之, 所有的点都在某条重链上(重链可能是一个点)

现在, 我们可以通过 d f s dfs dfs 求出以任意节点为根的子树大小, 并且通过全部比较找出每个节点的重儿子.
之后再次 d f s dfs dfs , 把重儿子连成一条又一条链, 记录每个节点所在链的链顶, 等会 L C A LCA LCA 要用

//知道原理就自己写吧, 我的码风一言难尽, 学长写了50行, 我写了270行, 删完之后还剩150行
void dfs(int now,int father){
	for(int x=0;x<g[now].size();x++){
		node to=g[now][x];
		if(to.v==father){
			continue;
		}
		fa[to.v]=now;
		dfs(to.v,now);
		siz[now]+=siz[to.v];
	}
	return;
}

void dfss(int now,int father){
	int num=0,maxn=-1;
	for(int x=0;x<g[now].size();x++){
		node to=g[now][x];
		if(to.v==father){
			continue;
		}
		if(maxn<siz[to.v]){
			maxn=siz[to.v];
			num=to.v;
		}
	}
	for(int x=0;x<g[now].size();x++){
		node to=g[now][x];
		if(to.v==father){
			continue;
		}
		if(to.v==num){
			chain_top[to.v]=chain_top[now];
		}else{
			chain_top[to.v]=to.v;
		}
	}
	for(int x=0;x<g[now].size();x++){
		if(g[now][x].v==father){
			continue;
		}
		dfss(g[now][x].v,now);
	}
	return;
}

现在树剖完了, 就该 L C A LCA LCA

树链剖分LCA

有两个节点 A , B A,B A,B ,求它们最近公共祖先
L C A LCA LCA 的大致思想就是不断地往上跳, 直到两个节点到根的路径成包含关系时停止. 但是这个往上跳的过程很费时间, 于是我们可以通过某些判断, 让节点往上跳一条重链的长度(如图)
这棵树是挺大的在这里插入图片描述
假设现在我要求 L C A ( 15 , 19 ) LCA(15,19) LCA(15,19) 以下是具体操作步骤

  • 判断当前两个节点的链顶是不是同一个点
  • 若是, 则输出两个点中深度小的那个
  • 若不是, 就让链顶深度最大的那个点往上跳, 跳到链顶的父亲节点
  • 重复上述过程, 直到判断为是

要是没太明白的话就自己手动模拟以下:

int LCA(int a,int b){
	while(ct[a]!=ct[b]){
		if(dep[chain_top[a]]>=dep[chain_top[b]]){
			a=fa[chain_top[a]];
		}else{
			b=fa[chain_top[b]];
		}
	}
	if(dep[a]<dep[b]){
		return a;
	}else{
		return b;
	}
}

个人认为这个比倍增 L C A LCA LCA 好理解

例题

今天晚上更


http://www.kler.cn/news/311765.html

相关文章:

  • Redis实践之缓存:设置缓存过期策略
  • 计算机网络33——文件系统
  • sqli-labs靶场自动化利用工具——第13关
  • RabbitMQ 和 Kafka 的详细对比表格
  • 消息队列:如何确保消息不会丢失?
  • 自然语言处理实战项目全解析
  • 阻止冒泡事件
  • Python中的异步编程:从基础知识到高级应用
  • vi | vim基本使用
  • 视频相关处理
  • 基于Delphi的题库生成系统
  • spark读mongodb
  • HTB-Jerry(tomcat war文件、msfvenom)
  • Unity制作角色溶解变成光点消失
  • GPT提示词分享 —— 深度思考助手
  • 【Vue】VueRouter路由
  • Spring系统学习(一)——初识Spring框架
  • 第五届“马栏山杯”国际音视频算法大赛创新应用赛投票环节正式启动啦!
  • Json和Http专栏
  • linux如何查看当前的目录所在位置
  • GDPU 信息安全 天码行空1 用Wireshark分析典型TCP/IP体系中的协议
  • 【vue】vue3+ts对接科大讯飞大模型3.5智能AI
  • MongoDB的安装和使用
  • React Zustand状态管理库的使用
  • 性能优化一:oracle 锁的原则
  • 手机实时提取SIM卡打电话的信令和声音-新的篇章(一、可行的方案探讨)
  • 【简单记录】Linux系统安装ZooKeeper
  • 【电路笔记】-运算放大器比较器
  • 在线查看 Android 系统源代码 Git repositories on android
  • YOLOv9改进策略【注意力机制篇】| MCAttention 多尺度交叉轴注意力