树链剖分原理和实现

树链剖分原理和实现.md

树链剖分原理和实现

 

理解

 

树链剖分就是将树分割成多条链,然后利用数据结构(线段树、树状数组等)来维护这些链。

首先就是一些必须知道的概念:

  • 重结点:子树结点数目最多的结点;
  • 轻节点:父亲节点中除了重结点以外的结点;
  • 重边:父亲结点和重结点连成的边;
  • 轻边:父亲节点和轻节点连成的边;
  • 重链:由多条重边连接而成的路径;
  • 轻链:由多条轻边连接而成的路径;

树链剖分

比如上面这幅图中,用黑线连接的结点都是重结点,其余均是轻结点,2-11、1-11就是重链,其他就是轻链,用红点标记的就是该结点所在链的起点,也就是我们👇提到的top结点,还有每条边的值其实是进行dfs时的执行序号。

算法中定义了以下的数组用来存储上边提到的概念:

名称解释
siz[u]保存以u为根的子树节点个数
top[u]保存当前节点所在链的顶端节点
son[u]保存重儿子
dep[u]保存结点u的深度值
faz[u]保存结点u的父亲节点
tid[u]保存树中每个节点剖分以后的新编号(DFS的执行顺序)
rnk[u]保存当前节点在树中的位置

 

除此之外,还包括两种性质:

  1. 如果(u, v)是一条轻边,那么size(v) < size(u)/2;
  2. 从根结点到任意结点的路所经过的轻重链的个数必定都小与O(logn);

 

首先定义以下数组:

 

 

算法大致需要进行两次的DFS,第一次DFS可以得到当前节点的父亲结点(faz数组)、当前结点的深度值(dep数组)、当前结点的子结点数量(size数组)、当前结点的重结点(son数组)

 

 

 

第二次DFS的时候则可以将各个重结点连接成重链,轻节点连接成轻链,并且将重链(其实就是一段区间)用数据结构(一般是树状数组或线段树)来进行维护,并且为每个节点进行编号,其实就是DFS在执行时的顺序(tid数组),以及当前节点所在链的起点(top数组),还有当前节点在树中的位置(rank数组)。

 

 

而修改和查询操作原理是类似的,以查询操作为例,其实就是个LCA,不过这里使用了top来进行加速,因为top可以直接跳转到该重链的起始结点,轻链没有起始结点之说,他们的top就是自己。需要注意的是,每次循环只能跳一次,并且让结点深的那个来跳到top的位置,避免两个一起跳从而插肩而过。

 

 

 

实战

 

以这道题目为例,可以看出算法大致有两种操作,分别是求任意两个节点所连接的路径和、极值,又或者是以任意一个节点作为跟节点来求与子结点的路径和、极值,而求区间和、区间极值正是线段树所擅长的。

首先要构建线段树:

 

 

posted @ 2017-11-12 12:21  banananana  阅读(18173)  评论(11编辑  收藏  举报