“如果你会了树上dp,还会线段树……”
“没错!我都会啊!”
“……那你为什么写不出树链剖分?”
“???”
——by勇者和路由器的对话,今天二位仍然过得十分愉快
————————————————————————————
因为路由器编不出来什么好题面了,所以就扔上来了一个模板题然后和勇者去玩了。
(下面有树链剖分的板子代码+注释,这里只提供讲解)
[ZJOI2008]树的统计Count:http://www.cnblogs.com/luyouqi233/p/7886531.html
我们发现题中要求的内容类似于线段树:单点修改,区间询问。
但是,这是一棵树啊!我们怎么才能在树上建一个只适用于一维的数据结构呢?
我们要抛弃线段树吗?
……
那么我们要试图把树拍扁成一维的吗?
……只能这样了。
其实拍扁成一维并不难想,考虑当树为一条链的时候吗,我们就直接上线段树即可。
那么类比一棵完整的树时,我们就把它分解成一条一条链然后拼在一起线段树维护即可。
关键问题在于要如何分解成链才能使得我们查询既快捷又方便呢?
这里当然就是树链剖分的活啦!
树链剖分:
概念:
定义size(X)为以X为根的子树的节点个数。 令V为U的儿子节点中size值最大的节点,那么边(U,V)被称为重边,树中重边之外的边被称为轻边。
我们称某条路径为重路径,当且仅当它全部由重边组成。
性质:
性质 1:轻边(U,V),size(V)<=size(U)/2。
性质 2:从根到某一点的路径上,不超过O(logN)条轻边,不超过O(logN)条重路径。
对于性质1,我们肉眼观察法和反证法都能解决。
对于性质2就不是那么明显了,我们来证明一下:
由性质1可知,每经过一条轻边,子树的节点个数至少减少一半,所以至多经过 O (log n ) 条轻边。
而进入(或从……出去)一条重路径,一定需要经过一条轻边,所以至多经过 O (log n ) 条重路径。
有了以上两个性质之后,我们就可以发现这种分法的优越性了,我们仅仅只需要搜大概logn级别即可。
细节:
预处理:
我们具体需要求出7个值,分别为:
对于节点u:
父亲fa;
深度dep;
子树节点数size(又叫重量);
重儿子son;
所在重路径的顶部节点top;
在序列的位置pos(下标)。
对于序列的一个下标:
对应树的位置idx。
——————————————————
前四个朴素dfs即可解决,后三个根据节点的重儿子再dfs即可解决。
注意:我们的目的是为了将树分解成重路径,所以第二次dfs建序列的时候要先加重儿子再管其他节点。
求值:
我们将u到v的路径分解成:
当u与v不在同一个重路径时:
(u所在的部分重路径)+(top[u]到top[v])+(v所在的部分重路径)。
当u与v在同一个重路径时(显然不需要分解)
按照上面的方法递归并且不断求出这些段的值完后汇总即可。
那么我们就想先跳u为top[u]还是跳v为top[v]——方法就是,为了防止跳大了,跳得越少越好(比较top[u]和top[v]的dep即可)。
例题(难度递增):
BZOJ1036:[ZJOI2008]树的统计:http://www.cnblogs.com/luyouqi233/p/7886531.html
BZOJ2243:[SDOI2011]染色:http://www.cnblogs.com/luyouqi233/p/7891126.html
CC DGCD:Dynamic GCD:http://www.cnblogs.com/luyouqi233/p/7896160.html