关于链剖
链剖实质上是通过轻重链的划分,将树剖成一条条重链,首尾相连存进数据结构(线段树、树状数组、splay之类的),支持路径查询,修改等
如何实现?
定义以下几种东西
- 重儿子 子树大小最大的儿子
- 轻儿子 除重儿子其他的都是轻儿子
- 重边 每个点连向它的重儿子的边
- 轻边 连向轻儿子的边
- 重链 重边组成的链
这里总共有5条重链,1-2-4-8、5、9、3-6-10、7
我们按照优先重链的DFS序重新标号,用DFN[]数组记录,例如DFN[8]=4,DFN[7]=10
定义几个数组top[],son[],deep[],size[],分别表示所在重链顶端,重儿子编号,深度,子树大小
我们可以一次DFS求出son[],deep[],sizep[]
再一次DFS求出top[],dfn[],这两个DFS非常简单,自己随便YY一下
然后就可以根据DFN的顺序依次丢进数据结构里面。
重点在于,如何路径修改和路径查询
一般的都是要做一个LCA的,然而链剖并不需要
设当前要修改的路径起点u,终点v
如果u,v在同一条重链上,即top[u]=top[v],那么可以直接在数据结构上区间修改dfn[u]到dfn[v]这一段。
如果u,v不在同一条重链上,需要比较deep[top[u]],deep[top[v]]
不妨总是使deep[top[u]]>=deep[top[v]],不是的话就交换
那么我们区间修改dfn[top[u]]到dfn[u],再把u跳到father[top[u]]
重复操作,直到u=v
查询也是一样的。
复杂度是O(Nlog2N)的
下面有一个例题
[ZJOI2008][JZOJ2256][BZOJ1036]树的统计
代码见这里