树链剖分原理和实现

树链剖分原理和实现

 

理解

 

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

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

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

树链剖分

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

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

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

 

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

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

 

首先定义如下数组:算法

 
  
  
  
  

 

算法大体须要进行两次的DFS,第一次DFS能够获得当前节点的父亲结点(faz数组)、当前结点的深度值(dep数组)、当前结点的子结点数量(size数组)、当前结点的重结点(son数组)windows

 

 
  
  
  
  

 

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

 
  
  
  
  

 

而修改和查询操做原理是相似的,以查询操做为例,其实就是个LCA,不过这里使用了top来进行加速,由于top能够直接跳转到该重链的起始结点,轻链没有起始结点之说,他们的top就是本身。须要注意的是,每次循环只能跳一次,而且让结点深的那个来跳到top的位置,避免两个一块儿跳从而插肩而过。数组

 

 
  
  
  
  

 

实战

 

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

首先要构建线段树:

 
  
  
  
  
相关文章
相关标签/搜索