尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
树链剖分(树剖)算法详解:从原理到实现
1. 什么是树链剖分树链剖分Tree Chain Partition简称树剖是一种将树形结构转化为线性序列的算法技巧。它通过将树上的路径分解为若干条“重链”使得原本在树上难以高效处理的路径查询、路径修改等问题能够借助线段树、树状数组等数据结构在 O(log²n) 或 O(log n) 的时间复杂度内解决。树剖的核心思想是通过两次 DFS 预处理将树上的节点重新编号使得每条重链上的节点编号连续。这样树上的任意一条路径都可以被拆分成 O(log n) 段连续的区间从而可以用维护序列的数据结构来处理。2. 树链剖分的核心概念2.1 基本定义重儿子Heavy Son对于节点 u 的所有儿子中子树大小最大的那个儿子如果有多个任选一个。轻儿子Light Son除重儿子外的其他儿子。重边Heavy Edge连接节点与其重儿子的边。轻边Light Edge连接节点与其轻儿子的边。重链Heavy Chain由重边连续连接形成的极大路径。2.2 重要数组预处理结果fa[u]节点 u 的父节点。dep[u]节点 u 的深度根节点深度为 0 或 1。size[u]以 u 为根的子树大小。son[u]节点 u 的重儿子如果没有则为 0。top[u]节点 u 所在重链的顶端节点。dfn[u]节点 u 在 DFS 序中的新编号时间戳。rnk[dfn[u]]DFS 序编号对应的原节点即 rnk[dfn[u]] u。3. 树链剖分的预处理两次 DFS3.1 第一次 DFS计算父节点、深度、子树大小、重儿子void dfs1(int u, int father) { fa[u] father; dep[u] dep[father] 1; size[u] 1; son[u] 0; for (int v : g[u]) { if (v father) continue; dfs1(v, u); size[u] size[v]; if (size[v] size[son[u]]) { son[u] v; } } }3.2 第二次 DFS进行重链剖分分配 DFS 序int tim 0; void dfs2(int u, int tp) { top[u] tp; dfn[u] tim; rnk[tim] u; // 优先遍历重儿子保证重链上节点 DFS 序连续 if (son[u]) { dfs2(son[u], tp); } // 遍历轻儿子轻儿子自己作为新重链的顶端 for (int v : g[u]) { if (v fa[u] || v son[u]) continue; dfs2(v, v); } }4. 路径查询与修改树剖最经典的应用查询或修改树上两点 u, v 之间路径上的节点权值和或最大值等。核心操作不断将深度较大的点向上跳每次跳一整条重链并将这条重链对应的区间dfn[top[u]] 到 dfn[u]进行查询/修改。// 假设有线段树 seg 可以处理区间 [l, r] 的查询/修改 int query_path(int u, int v) { int res 0; while (top[u] ! top[v]) { if (dep[top[u]] dep[top[v]]) swap(u, v); // 处理 u 所在的重链区间 [dfn[top[u]], dfn[u]] res seg.query(1, 1, n, dfn[top[u]], dfn[u]); u fa[top[u]]; // 跳到上一条重链 } // 此时 u, v 在同一条重链上 if (dep[u] dep[v]) swap(u, v); res seg.query(1, 1, n, dfn[u], dfn[v]); return res; }5. 子树查询与修改由于 DFS 序的性质以 u 为根的子树中所有节点的新编号 dfn 是连续的区间 [dfn[u], dfn[u] size[u] - 1]。因此子树操作可以直接转化为区间操作// 查询子树 u 的权值和 int query_subtree(int u) { return seg.query(1, 1, n, dfn[u], dfn[u] size[u] - 1); } // 修改子树 u 中所有节点的权值加上 val void update_subtree(int u, int val) { seg.update(1, 1, n, dfn[u], dfn[u] size[u] - 1, val); }6. 时间复杂度分析预处理两次 DFSO(n)。路径操作每次跳转将当前节点 u 跳到 fa[top[u]]由于从叶子到根最多经过 O(log n) 条轻边每经过一条轻边子树大小至少翻倍因此路径会被拆分成 O(log n) 条重链区间。若区间操作线段树为 O(log n)则总复杂度为 O(log²n)。子树操作O(log n)线段树区间操作。7. 典型例题与代码模板例题给定一棵 n 个节点的树每个节点有一个权值。需要支持两种操作将节点 u 到节点 v 的路径上所有节点权值加上 val。查询节点 u 到节点 v 的路径上所有节点权值之和。完整代码模板较长此处给出核心结构#include bits/stdc.h using namespace std; const int N 1e5 5; vectorint g[N]; int fa[N], dep[N], size[N], son[N]; int top[N], dfn[N], rnk[N], tim; int w[N]; // 原权值 int nw[N]; // 按 DFS 序排列的权值 // 线段树部分略 struct SegTree { ... } seg; void dfs1(int u, int f) { ... } void dfs2(int u, int tp) { ... } void update_path(int u, int v, int val) { while (top[u] ! top[v]) { if (dep[top[u]] dep[top[v]]) swap(u, v); seg.update(1, 1, n, dfn[top[u]], dfn[u], val); u fa[top[u]]; } if (dep[u] dep[v]) swap(u, v); seg.update(1, 1, n, dfn[u], dfn[v], val); } int query_path(int u, int v) { int res 0; while (top[u] ! top[v]) { if (dep[top[u]] dep[top[v]]) swap(u, v); res seg.query(1, 1, n, dfn[top[u]], dfn[u]); u fa[top[u]]; } if (dep[u] dep[v]) swap(u, v); res seg.query(1, 1, n, dfn[u], dfn[v]); return res; } int main() { // 读入树 // 第一次 DFSdfs1(root, 0) // 第二次 DFSdfs2(root, root) // 将原权值 w[u] 按 DFS 序存入 nw[dfn[u]] // 建线段树 seg.build(1, 1, n, nw) // 处理询问 return 0; }8. 总结与扩展树链剖分的优势将树上路径问题转化为序列区间问题可以套用丰富的序列数据结构。预处理 O(n)单次路径操作 O(log²n)在大多数题目中足够高效。思想清晰模板性强学会后可以解决一大类树上路径问题。常见变体与应用边权转点权将边权赋给深度较大的端点查询时注意 LCA 处权值不计入。结合树状数组如果只有单点修改、区间查询可以用树状数组代替线段树。维护路径最值将线段树的求和改为求最大值/最小值。结合可持久化线段树实现树上路径第 k 大等查询。树链剖分是算法竞赛中处理树上路径问题的利器理解其“重链剖分区间维护”的核心思想后便能灵活应用于各种变式题目。
RELATED

相关推荐

AI生成内容检测工具与算法更新的技术博弈

AI生成内容检测工具与算法更新的技术博弈

1. 降AI工具与算法更新的速度博弈上周帮客户调试一个老牌降AI工具时,发现它对最新发布的Stable Diffusion 3模型支持度几乎为零。这已经是今年第三次遇到类似情况——当我在GitHub上提交issue时,开发者无奈回复:"算法更新太快&#xff0…

📅 2026/9/8 0:17:28
OpenClaw开源智能体网关:AI助手与即时通讯的完美融合

OpenClaw开源智能体网关:AI助手与即时通讯的完美融合

1. 项目概述:当AI助手遇上即时通讯上周在调试一个自动化工作流时,我突然意识到:如果能把AI助手直接集成到日常使用的聊天软件里,很多重复性工作就能在对话中一键完成。这个想法促使我找到了OpenClaw——一个开源的智能体网关项目&…

📅 2026/9/13 4:47:52
谷歌财报技术解析:云原生与AI驱动业绩增长及开发者机遇

谷歌财报技术解析:云原生与AI驱动业绩增长及开发者机遇

最近在分析科技公司财报时,发现谷歌母公司 Alphabet 2026 财年第二财季的业绩表现格外亮眼,归母净利润达到 1121.07 亿美元,同比增长 298%。作为开发者,我们不仅要关注技术本身,更要理解背后的商业逻辑和行业趋势。本文…

📅 2026/8/22 20:24:22
MORE NEWS

更多资讯

📰

CCGS `/setup-engine` 技能深度解析:通过 `technical-preferences.md` 一键配置引擎、语言与专家路由

CCGS /setup-engine 技能深度解析:通过 technical-preferences.md 一键配置引擎、语言与专家路由 【免费下载链接】Claude-Code-Game-Studios Turn Claude Code into a full game dev studio — 49 AI agents, 72 workflow skills, and a complete coordination sys…

📰

学术写作中如何降低AI生成内容检测率

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

C语言嵌入式植物数据库设计与内存优化实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

RP2040 MicroPython 实现内存到内存DMA传输

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

C++ map遍历的三种写法:性能、安全与兼容性实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

OpenSEO:开源 Semrush/Ahrefs 替代方案的数据源、自托管与 MCP Agent 集成实战

OpenSEO:开源 Semrush/Ahrefs 替代方案的数据源、自托管与 MCP Agent 集成实战 【免费下载链接】open-seo Open source alternative to Semrush and Ahrefs 项目地址: https://gitcode.com/GitHub_Trending/op/open-seo OpenSEO 是一个定位为"开源版 Semrush/Ahref…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬