算法学习笔记(6): 树链剖分

算法,学习,笔记 · 浏览次数 : 39

小编点评

**树链剖分目录树链剖分性质?发现?具体用法树链剖分是一个很神奇,但是在树上可以完成一些区间操作问题简单来说,就是把一棵树分成一条条的链,通过维护链上的信息来维护整棵树的信息基础知识可以参考我的另外一篇博客:算法学习笔记(5): 最近公共祖先(LCA) **发现?** 当我们动手模拟寻找dfn的步骤时,我们不难发现,同一条链上的dfn序是连续的意味着我们可以通过结点的dfn序来维护其信息,因为dfn序是节点在链上的深度排序,两个节点的深度差值一定是1。 **具体用法树链剖分** 树链剖分是一种算法,用于将一棵树分成一条条的链,并维护该链上的信息,以便在不修改原树的情况下,进行一些区间操作。 **树链剖分的性质** * 每个节点的深度在链中的排名由其在链中的位置决定。 * 每个节点的父亲在链中的排名比其子节点的排名高。 * 对于一个节点,其子节点的深度最大为其自身深度减1。 **树链剖分的应用** 树链剖分可以用于解决一些问题,例如: * 查询树中任意两个节点的最近公共祖先。 * 修改树中任意两个节点的路径上的权值。 * 统计树中的所有叶子节点的深度。 **模板题:轻重链剖分/树链剖分 - 洛谷** 这篇文章以模板题【模板】轻重链剖分/树链剖分 - 洛谷为例,详细介绍了树链剖分的算法和代码实现。

正文

树链剖分

树链剖分是一个很神奇,但是在树上可以完成一些区间操作问题

简单来说,就是把一棵树分成一条条的链,通过维护链上的信息来维护整棵树的信息

基础知识可以参考我的另外一篇博客:算法学习笔记(5): 最近公共祖先(LCA)

这里假设你已经掌握了上述博客中的所有相关知识,并清晰了其背后的原理


性质?发现?

当我们动手模拟寻找dfn的步骤时,我们不难发现,同一条链上的dfn序是连续的

意味着我们可以通过结点的dfn序来维护其信息

由于涉及到区间修改或者查询之类的问题,一般来说需要用到线段树的辅助

如果不明白线段树的话,建议不要食用本文


具体用法

我们以模板题:【模板】轻重链剖分/树链剖分 - 洛谷为例

由于我们需要修改两个点的路径上点的权值,依据上文我们已经知道在链上的dfn序是连续的,很明显,我们需要找到两个点路径之间所有在同一条链上的部分进行区间修改

联想到树链剖分求LCA的过程,我们只需要在两个点向上跳的时候顺便维护其信息即可

类C伪代码可以参考如下

void change(int x, int y, data...) {
    // 不在同一条链上!
    while (top[x] != top[y]) {
        // 保证top较低的点先跳,防止跳飞
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        modify(dfn[top[x]], dfn[x], data...);
        x = fa[top[x]];
    }
    // 保证x的dfn较小
    if (dep[x] > dep[y]) swap(x, y);
    modify(dfn[x], dfn[y], data...);
}

modify(x, y, data...)指的是根据data维护区间[x, y]上的信息

注意:如果使用了线段树,在建树的时候一定要注意初始值。我指的是点dfn[x]上的值才是x的初始值。

如果你发现你似乎需要写两个change函数,但是你又没想到可以替代的名字

可以采用把线段树的代码用namespace包含进去(我常用seg作为namespace的名字,下文我会沿用这个名字)

具体来说,例如:

namespace seg {
    线段树要的数据...;
    void build(...) { ... }
    void change(...) { ... }
    void query(...) { ... }
    ....
}

在namespace之外,就可以采用seg::change(...)之类没有歧义的写法了

这样很好的避免了词汇匮乏命名混乱的问题

复杂度:自然还是要分析一波。由于树链剖分最多会分成logN个区间,每个区间的操作基于线段树区间修改的复杂度,所以复杂度为\(O(Nlog^2N)\)


这个部分……讲的好少啊

管他的,下课!

与算法学习笔记(6): 树链剖分相似的内容:

算法学习笔记(6): 树链剖分

树链剖分 树链剖分是一个很神奇,但是在树上可以完成一些区间操作问题 简单来说,就是把一棵树分成一条条的链,通过维护链上的信息来维护整棵树的信息 基础知识可以参考我的另外一篇博客:算法学习笔记(5): 最近公共祖先(LCA) 这里假设你已经掌握了上述博客中的所有相关知识,并清晰了其背后的原理 性质?发

算法学习笔记(11): 原根

原根 此文相对困难,请读者酌情食用 在定义原根之前,我们先定义其他的一点东西 阶 通俗一点来说,对于 $a$ 在模 $p$ 意义下的阶就是 $a^x \equiv 1 \pmod p$ 的最小正整数解 $x$ 或者说,$a$ 在模 $p$ 意义下生成子群的阶(群的大小) 再或者说,是 $a$ 在模

算法学习笔记(30):Kruskal 重构树

Kruskal 重构树 这是一种用于处理与最大/最小边权相关的一个数据结构。 其与 kruskal 做最小生成树的过程是类似的,我们考虑其过程: 按边权排序,利用并查集维护连通性,进行合并。 如果我们在合并时,新建一个节点,其权值为当前处理的边的权值,并将合并的两个节点都连向新建的节点,那么就可以得

算法学习笔记(3.1): ST算法

ST表 在RMQ(区间最值)问题中,著名的ST算法就是倍增的产物。ST算法可以在 \(O(n \log n)\) 的时间复杂度能预处理后,以 \(O(1)\) 的复杂度在线回答区间 [l, r] 内的最值。 当然,ST表不支持动态修改,如果需要动态修改,线段树是一种良好的解决方案,是 \(O(n)\

C++算法之旅、09 力扣篇 | 常见面试笔试题(上)算法小白专用

算法学习笔记,记录容易忘记的知识点和难题。详解时空复杂度、50道常见面试笔试题,包括数组、单链表、栈、队列、字符串、哈希表、二叉树、递归、迭代、分治类型题目,均带思路与C++题解

C++算法之旅、08 基础篇 | 质数、约数

算法学习笔记,记录容易忘记的知识点和难题。试除法、分解质因数、筛质数、约数个数、约数之和、最大公约数

算法学习笔记(1): 欧几里得算法及其扩展

扩展欧几里得算法详解 在了解扩欧之前我们应该先了解欧几里得算法 欧几里得算法 这是一个递归求最大公约数(greatest common divisor)的方法 $$ gcd(a, b) = gcd(b, a % b) $$ 可以通过一个类似的简单公式推导而来 好像叫做辗转相减法来着? $$ gcd(

算法学习笔记(3): 倍增与ST算法

倍增 目录倍增查找 洛谷P2249重点变式练习快速幂ST表扩展 - 运算扩展 - 区间变式答案倍增更多的用法优化矩形查询优化建图优化 DP作者有话说 倍增,字面意思即”成倍增长“ 他与二分十分类似,都是基于”2“的划分思想 那么具体是怎么样,我们以一个例子来看 查找 洛谷P2249 依据题面,我们知

算法学习笔记(4): 并查集及其优化

# 并查集 并查集,Disjoint-Set,或者通俗一点,叫做MergeFind-Set,是一种可以动态维护若干个不重叠的集合,并支持集合之间的合并与查询的数据结构。 集体来说,并查集支持下列两个操作: - Find,查询元素所属集合 - Merge,将两个元素所属集合合并 一般来说,为了具体实现

算法学习笔记(5): 最近公共祖先(LCA)

树上最近公共祖先(LCA)三种求法:倍增,DFS+ST表,熟练剖分