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

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

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

首先 在学树链剖分之前最好先把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

时间: 2024-11-06 16:54:52

树链剖分(轻重链剖分)算法笔记[转]的相关文章

树链剖分简(单)介(绍)

树链剖分可以算是一种数据结构(一大堆数组,按照这个意思,主席树就是一大堆线段树).将一棵树分割成许多条连续的树链,方便完成一下问题: 单点修改(dfs序可以完成) 求LCA(各种乱搞也可以) 树链修改(修改任意树上两点之间的唯一路径) 树链查询 (各种操作)  前两个内容可以用其他方式解决,但是下面两种操作倍增.st表,dfs序就很难解决(解决当然可以解决,只是耗时长点而已).下面开始步入正题. 树链剖分的主要目的是分割树,使它成一条链,然后交给其他数据结构(如线段树,Splay)来进行维护.常

树链剖分学习笔记

先让我们看一个题目 有一棵n个节点的树,树的每条边有个边权,有如下两种操作 1.修改一条边的边权 2.查询两点之间路径的权值 对于这种题目,可能有人会选择直接暴力,这很明显不行. 换一种思路,如果我们把树的每一条边拆下来,对他们进行编号,然后使用线段树来存储呢?使用线段树来对每条边的边权进行修改和查询是很方便的.于是这样我们就引出了树链剖分. 树链剖分其实就是把一棵树上的各个边拆开来进行处理,从而对树的整体进行划分. 将树划分为链,用数据结构来维护这些链,时间复杂度大致为O(log n) . 在

[学习笔记]树链剖分

基本思想 树链剖分一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每条边属于且只属于一条链,然后再通过数据结构来维护每一条链. 一些定义 树链:树上的路径. 剖分:把路径分类为重链和轻链. 重儿子:u的子节点中siz[v]值最大的v. 轻儿子:u的其它子节点. 重边:点u与其重儿子的连边. 轻边:点u与其轻儿子的连边. 重链:由重边连成的路径. 轻链:轻边. 性质 如果(u,v)为轻边,则siz[v]$\times$2<siz[u]. 从根到某一点的路径上轻链.重链的个数都不大于l

【bzoj4999】This Problem Is Too Simple! 树链剖分+动态开点线段树

题目描述 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x<2^31) 表示询问i节点到j节点的路径上有多少个值为x的节点. 输入 第一行有两个整数N,Q(1 ≤N≤ 100,000:1 ≤Q≤ 200,000),分别表示节点个数和操作个数. 下面一行N个整数,表示初始时每个节点的初始值. 接下来N-1行,每行两个整数x,y,表示x节点与y节点之间有边直接相连(描述一颗树).

HDU 5029 树链剖分

Relief grain Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 100000/100000 K (Java/Others) Total Submission(s): 861    Accepted Submission(s): 219 Problem Description The soil is cracking up because of the drought and the rabbit kingdom is f

【BZOJ 3531】【SDOI 2014】旅行 树链剖分

因为有$10^5$个宗教,需要开$10^5$个线段树. 平时开的线段树是“满”二叉树,但在这个题中代表一个宗教的线段树管辖的区间有很多点都不属于这个宗教,也就不用“把枝叶伸到这个点上”,所以这样用类似主席树的数组动态开点来建立$10^5$个只有几个“树枝”的线段树,维护轻重链就可以了 线段树的$L,R,l,r$弄反了调了好久$QAQ$ $so$ $sad$ #include<cstdio> #include<cstring> #include<algorithm> #d

BZOJ 1036: [ZJOI2008]树的统计Count 【树链剖分】

Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 Input 输入的第一行为一个整数n,表示节点的个数.接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条

并不对劲的树链剖分

听上去像是熟练剖粪. 一棵树可以看成是很多条链组成的.那么把这些链拼成一条线,在树上进行区间操作时就可以将每次操作的部分拆成很多段连续的部分.这样就可以用线段树什么的维护了.那么问题又来了,如果瞎拆的话,可能会有一次操作涉及的链很倒霉,每一个点都被断成了一部分.这样就不得不用链的长度的时间复杂度来执行此次操作.能不能让每条路径被断的次数小一些呢?想必是能的,这要用到轻重链剖分. 重链上的点在线段树上是一段连续是空间,所以要使每条路径上的点少一些.大致想一个贪心策略,可以尽量往点多的地方分重链.这

bzoj1036: [ZJOI2008]树的统计Count(树链剖分+线段树维护)

bzoj1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MB Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的