码迷,mamicode.com
首页 > 编程语言 > 详细

树链剖分(轻重链剖分)算法笔记[转]

时间:2017-09-23 15:26:57      阅读:204      评论:0      收藏:0      [点我收藏+]

标签:必须   部分   映射   节点   git   结束   nlog   对比   span   

仔细想想 自己第一次听说这个这个数据结构大概有两年半的时间了 然而一直不会.

不过现在再回头来看 发现其实也不是很麻烦

首先 在学树链剖分之前最好先把LCALCA 树形DPDP 以及dfsdfs序 这三个知识点学了

如果这三个知识点没掌握好的话 树链剖分难以理解也是当然的

-------------------------------------------------------------------------------------------

树链剖分通常用于处理树的形态不变 但点权/边权需要修改查询的题目

在选定一个点作为根后 我们来对这棵树进行操作

 

第一步

 

从根开始进行一遍遍历((通常用dfs)dfs) 这一遍遍历需要统计以下信息

父亲节点fafa 当前节点深度dd 子树大小szsz 这些显然是在学习前置知识时已经用得比较熟练了的

然后 在这一遍遍历中 我们需要再求当前节点的重儿子sonson

重儿子的定义是 当前节点的子节点中 子树大小最大的那个 ((如果有多个任取一个))

其余的就是轻儿子了

 

另外所有节点与其重/轻儿子的连边称为重/轻边

把连续的重边定义为重链

我们会发现这样一个性质

从任一节点向根节点走 走过的重链和轻边的条数都是loglog级别以内的

 

证明如下:

由于任一轻儿子对应的子树大小要小于父节点所对应子树大小的一半

因此从一个轻儿子沿轻边向上走到父节点后 所对应的子树大小至少变为两倍以上

经过的轻边条数自然是不超过log2Nlog2N的

然后由于重链都是间断的 ((连续的可以合成一条))

所以经过的重链的条数是不超过轻边条数+1+1的

因此经过重链的条数也是loglog级别的

综合可知原命题得证

 

从轻边向上走显然每条轻边都可以做到O(1)O(1)向上走

而从重链向上走要做到每条重链只用O(1)O(1)就必须额外做一些处理

 

第二步

 

利用第一遍遍历得到的信息 我们再进行一遍遍历((需用dfs)dfs)

对于每个重儿子 要求出沿重链向上走走到顶端的点的位置toptop

这个toptop显然是和父节点的一样的

对于每个轻儿子 由于向上走的重链不存在 我们可以令toptop为自身

现在从重链向上走都只需要O(1)O(1)了

不过修改的复杂度仍然不靠谱

对于两点之间的修改和询问操作 轻边我们可以暴力直接修改询问 重链则必须结合一些数据结构进行以保证效率

 

这个时候 我们或许会回想起学dfsdfs序的时候 我们将树上的点根据dfs序映射到了一维数组上

从而可以利用线段树等数据结构对在dfsdfs序上连续的一段区间进行修改和询问

因此 为了能够用线段树等数据结构进行维护 我们必须将同一条重链上的点映射到一个连续的区间

这个操作并不复杂 我们只需在对每个点dfsdfs时先遍历到它的重儿子 最后重链上的点映射到一维数组里便是连续的

 

做完这两个步骤后 树链剖分的核心部分就结束了

不过注意两个点向上走的时候 不能走得超过这两个点的LCALCA

这里的具体操作最好独立思考一下 对比倍增LCALCA的写法会更好理解

 

看到这里 大家大概也能反应到树链剖分的复杂度是O(NlogN+Qlog2N)

转自http://www.cnblogs.com/sagitta/p/5660749.html

树链剖分(轻重链剖分)算法笔记[转]

标签:必须   部分   映射   节点   git   结束   nlog   对比   span   

原文地址:http://www.cnblogs.com/whc200305/p/7581034.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!