【板子】LCA 树链剖分

发布时间:2026/7/28 10:17:31

【板子】LCA 树链剖分
这是另一种非常经典的求解最近公共祖先LCA的方法树链剖分Heavy-Light Decomposition。与Tarjan 算法离线算法不同树链剖分是一种在线算法。1. 核心概念什么是“重”和“轻”树链剖分的核心思想是将一棵树切分成若干条链使得在查找路径时效率最高。为了实现这一点它定义了以下概念重儿子 (Heavy Son)对于节点 u它的所有子节点中子树节点数量最多的那个儿子。轻儿子 (Light Son)除了重儿子以外的所有儿子。重边 (Heavy Edge)连接节点 u 和它的重儿子的边。轻边 (Light Edge)连接节点 u 和它的轻儿子的边。重链 (Heavy Chain)由多条重边连接而成的路径。性质任意一条树上的路径最多只会被切成 logn 条链。这就是为什么它速度快的原因。2. 需要维护的数组为了实现树链剖分我们需要维护以下几个关键数组fa[u]节点 u 的父节点。dep[u]节点 u 的深度根节点深度通常为 1。sz[u]以 u 为根的子树的节点总数。son[u]节点 u 的重儿子如果没有则为 0。top[u]节点 u 所在重链的顶端节点。3. 算法流程与代码模板树链剖分求 LCA 的过程分为两个阶段两次 DFS 预处理​ 和在线查询。第一阶段预处理两遍 DFS第一遍 DFS (dfs1) 负责计算子树大小、父节点、深度和重儿子。第二遍 DFS (dfs2) 负责给节点分配链顶top将树真正剖分成链。#include iostream #include vector #include cstring using namespace std; const int N 500010; vectorint e[N]; // 邻接表存图 // 树链剖分核心数组 int fa[N], dep[N], son[N], sz[N]; int top[N]; // 链顶数组 // 第一遍 DFS找重儿子、算大小、算深度 void dfs1(int u, int father) { fa[u] father; dep[u] dep[father] 1; sz[u] 1; son[u] 0; // 初始化没有重儿子 for (auto v : e[u]) { if (v father) continue; dfs1(v, u); sz[u] sz[v]; // 累加子树大小 // 更新重儿子如果当前儿子v的子树比之前记录的重儿子还大就更新 if (sz[v] sz[son[u]]) { son[u] v; } } } // 第二遍 DFS连重链、标记链顶 void dfs2(int u, int t) { top[u] t; // 记录当前点所在的链顶 if (son[u] 0) return; // 如果没有重儿子说明到底了 // 1. 优先递归处理重儿子重儿子的链顶和当前点一样 dfs2(son[u], t); // 2. 处理轻儿子轻儿子开启一条新的链 for (auto v : e[u]) { if (v fa[u] || v son[u]) continue; dfs2(v, v); // 新的链链顶就是自己 } } // 核心查询函数求 u 和 v 的 LCA int lca(int u, int v) { // 核心思想当两个点不在同一条重链上时让深度较大的那个点跳到链顶的父亲 while (top[u] ! top[v]) { // 优化总是让深的点往上跳减少代码行数 if (dep[top[u]] dep[top[v]]) swap(u, v); // 把 u 跳到链顶的父节点 u fa[top[u]]; } // 跳出循环时说明 u 和 v 在同一条重链上了 // 此时深度较小的那个点就是 LCA return dep[u] dep[v] ? u : v; } int main() { int n; // 节点数 cin n; // 读入 n-1 条边建图 for (int i 1; i n; i) { int a, b; cin a b; e[a].push_back(b); e[b].push_back(a); } // 初始化根节点信息并开始剖分 dfs1(1, 0); // 假设根为 1 dfs2(1, 1); // 处理查询 int q; // 查询次数 cin q; while (q--) { int u, v; cin u v; cout lca(u, v) endl; } return 0; }4. 原理解释如何求出 LCAlca函数的逻辑利用了“重链”的性质可以把它想象成在树上“走楼梯”不在同一条链上top[u] ! top[v]如果 u 和 v 不在同一条重链上说明它们之间有垂直的距离。我们总是让当前位置比较“深”dep大的那个点沿着它所在的重链一直往上爬直到到达链顶top。然后再从链顶跳到链顶的父节点u fa[top[u]]。这就相当于跨过了这条重链进入了另一条链。为什么要从轻儿子开始开新链​ 因为轻儿子的子树大小至少减半所以每经过一条轻边子树规模至少减少一半。这保证了从任意节点到根节点的路径上最多只有 logn 条轻边从而保证了跳跃次数是 logn 级别的。在同一条链上top[u] top[v]当循环结束说明 u 和 v 终于落在了同一条重链上。因为它们在同一条直线上所以位置靠下的那个点深度小的必然是另一个点的祖先。直接返回dep[u] dep[v] ? u : v即可。如图假设查询11911会沿着自己重链上升到4时9显然更深9已经是连顶跳到父节点4此时处于同一个链4为答案。

相关新闻

【板子】LCA Tarjan

【板子】LCA Tarjan

2026/7/28 10:17:31

这是一份基于 Tarjan(塔扬)算法的最近公共祖先(LCA)模板及详细讲解。该算法利用离线处理和并查集的思想,是目前求解 LCA 问题最高效的算法之一(时间复杂度 O(NQ),其中 N 为节点数,Q …

芯粒技术:半导体行业的成本与性能革命

芯粒技术:半导体行业的成本与性能革命

2026/7/28 10:17:31

1. 芯粒技术:半导体行业的范式革命当我在汽车电子实验室第一次拆解2024款智能汽车的域控制器时,一个指甲盖大小的模块引起了我的注意——这个集成了AI加速、传感器融合和通信功能的复杂系统,竟是由多个独立的小芯片像乐高积木一样拼接而成。这…

基于CH32x033的Arduino USB键盘开发:从环境搭建到HID设备实现

基于CH32x033的Arduino USB键盘开发:从环境搭建到HID设备实现

2026/7/28 10:17:31

1. 项目缘起:当Arduino遇上国产MCU的USB键盘梦最近在捣鼓一个桌面小工具,核心需求是想用一块小巧的开发板,通过USB接口模拟成一个键盘,自动执行一些按键序列,比如快速输入一串复杂的密码、或者一键打开某个软件组合。这…

【Bug已解决】[Bug]: DeepSeek-V4-Flash for L20,RuntimeError: Worker failed with error ‘AssertionError: aut

【Bug已解决】[Bug]: DeepSeek-V4-Flash for L20,RuntimeError: Worker failed with error ‘AssertionError: aut

2026/7/28 11:17:33

【Bug已解决】[Bug]: DeepSeek-V4-Flash for L20,RuntimeError: Worker failed with error AssertionError: auto_functionalized was not removed 解决方案 一、现象长什么样 在 L20(sm_89)上跑 DeepSeek-V4-Flash 时…

Java并发进阶系列:深度讨论高并发跳表数据结构ConcurrentSkipListMap的源代码实现(下)

Java并发进阶系列:深度讨论高并发跳表数据结构ConcurrentSkipListMap的源代码实现(下)

2026/7/28 11:17:33

文章说明:因为CSM解析内容较多,因此全文分为“深度讨论高并发跳表数据结构ConcurrentSkipListMap的源代码实现(上)”和“深度讨论高并发跳表数据结构ConcurrentSkipListMap的源代码实现(下)”两篇文章 上篇:CSM数据结构设计原理、doGet、doPut核心方法解析 下篇:doRemo…

Java并发进阶系列:深度讨论官方关于jdk1.8ConcurrentHashMap的computeIfAbsent源代码修复逻辑

Java并发进阶系列:深度讨论官方关于jdk1.8ConcurrentHashMap的computeIfAbsent源代码修复逻辑

2026/7/28 11:17:33

在文章中《深度解析官方关于jdk1.8的resizeStamp的bug处理过程》,我们讨论关于CHM的核心设计——resizeStam需要修复的处理过程,本文再次基于openJDK的bugs讨论组提出的CHM源代码另外一个会造成死循环的bug,默认读者已经掌握CHM的核心源代码实现,否则无法从本文的讨论中获益…

Java进阶系列:深度解析jdk1.8的HashMap红黑树balanceDeletion节点删除平衡算法设计(核心文章)

Java进阶系列:深度解析jdk1.8的HashMap红黑树balanceDeletion节点删除平衡算法设计(核心文章)

2026/7/28 11:17:33

这可能是全网最期待的jdk1.8的红黑树balanceDeletion的源代码解析技术文章! 其实掌握HashMap红黑树的同学都知道,balanceDeletion方法的源代码是HashMap红黑树部分最复杂也是最难理解的部分,目前少有coder对balanceDeletion有足够深入且可理解的分析,绝大部分关于深入Hash…

工业级负载控制方案:TPD2015FN与MKV42F128VLH16应用解析

工业级负载控制方案:TPD2015FN与MKV42F128VLH16应用解析

2026/7/28 11:17:33

1. 工业级负载控制方案概述在工业自动化、电力电子和高端设备控制领域,对电感和电阻负载的精确控制一直是工程师面临的核心挑战。TPD2015FN智能功率IC与MKV42F128VLH16微控制器的组合,为解决这一难题提供了可靠的技术方案。这套方案特别适用于需要高可靠…

物联网安全升级:SE050安全元件与TM4C1294NCPDT的完美结合

物联网安全升级:SE050安全元件与TM4C1294NCPDT的完美结合

2026/7/28 11:07:33

1. 物联网安全现状与SE050的定位在2023年的物联网安全态势报告中,全球每天新增的物联网设备达到惊人的150万台,但其中近70%的设备存在中高危安全漏洞。传统MCU方案(如STM32系列)虽然成本低廉,但在密钥存储、安全启动、…

[具身智能-649]:个人电脑搭建 RTSP 服务完整方案(Windows / Ubuntu 双平台,适配 RDK X5 rtsp2display 调试)

[具身智能-649]:个人电脑搭建 RTSP 服务完整方案(Windows / Ubuntu 双平台,适配 RDK X5 rtsp2display 调试)

2026/7/27 8:45:59

目标:电脑作为RTSP 服务端,循环推送 H264/H265 视频流; RDK X5 通过 rtsp2display 拉流预览,完全不需要在开发板编译 live555。 提供两套成熟方案: ✅ 方案 A:FFmpeg(最简单,优先推…

PDF合并与动态水印的工程化方案:2026国内免费工具实测对比

PDF合并与动态水印的工程化方案:2026国内免费工具实测对比

2026/7/27 8:42:17

一、背景与测试方案 在实际项目交付中,PDF文件合并与版权保护水印的叠加是一个高频但容易被低估的技术需求。典型的处理链路涉及:多源PDF的文件流合并、页面级水印渲染(含透明度混合与图层叠加)、输出文件体积控制。看似简单的操作…

PDF拆分压完图糊了?2026国内免费实测,档案员都在用的组合方案

PDF拆分压完图糊了?2026国内免费实测,档案员都在用的组合方案

2026/7/27 14:56:57

说实话,提到PDF拆分再压缩,我真是被折腾得够呛。 上个月公司年度合同归档,一份300多页的PDF总合同,需要按年份拆分成三个独立文件,再分别压缩到10MB以内方便邮件发送各部门确认。我心想这还不简单?先找个海…

零基础搭建桌面智能体,OpenClaw 2.7.9 分步实操,避开绝大多数部署陷阱

零基础搭建桌面智能体,OpenClaw 2.7.9 分步实操,避开绝大多数部署陷阱

2026/7/28 0:06:55

📌 一、工具核心优势盘点 数据本地存储,安全系数高所有操作日志、文档资料均保存在本机,不会上传至云端,能够有效保护企业文件与个人隐私,规避数据泄露风险。 上手简单,零编程门槛采用全图形化可视化界面&…

计算机毕业设计之基于springboot的购物平台设计与实现

计算机毕业设计之基于springboot的购物平台设计与实现

2026/7/28 0:06:55

由于移动应用技术的持续性的快速发展,现实生活中人们大多数都是通过移动手机、电脑等智能设备来完成生活中的事务。因此,许多的人工传统行业也开始与互联网结合,不再一味的依靠人工手动,努力打造半自动数字化甚至是全自动数字化模…

豆包AI绘图提示词失效真相:NLP模型层token截断机制首次披露,3招绕过字数限制

豆包AI绘图提示词失效真相:NLP模型层token截断机制首次披露,3招绕过字数限制

2026/7/28 0:06:55

更多请点击: https://codechina.net 第一章:豆包AI绘图提示词失效现象全景扫描 近期大量用户反馈,豆包(Doubao)AI绘图功能对常规提示词(Prompt)响应异常:语义明确的指令被忽略、中英…