(之前写过树剖的题,但没有完全理解,现在又无法复述思路了,所以重新学习一下,部分语句参考大神的叙述)
一、概念
树链剖分,顾名思义,树是由一根根树链组成的,我们现在要来把它按链来分解掉。我们知道,大部分树上的问题都是围绕树的路径来做文章,分解成一条条的链之后,我们就可以对节点(边)就行编号了,而同一根链,编号是连续的,这样就转化为了区间问题,我们就可以使用线段树等姿势来解决修改、求和、求最值等问题了(所以树剖不是一种数据结构)。
(盗图~~~)
二、怎样剖分
先描述几个概念和性质
重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子。
为了实现性质2,就必须要求我们剖分出轻重链,这个过程我们可以用两个dfs实现,记
fa[v] v的父节点
son[v] 与v同重链的儿子节点
pos[v] 编号,即对应线段树中的位置
size[v] 以v为根的子树中节点数
top[v] v所在链的顶端节点
第一次DFS,寻找重边
第二次DFS,编号
void dfs1(int x)
{
size[x] = 1;
son[x] = 0;
for(int i = h[x]; i; i = e[i].next)
{
dfs1(e[i].v);
if(size[e[i].v] > size[son[x]])
son[x] = e[i].v;
size[x] += size[e[i].v];
}
}
void split(int x)
{
if(son[fa[x]] == x)
top[x] = top[fa[x]];
else
{
top[x] = x;
for(int i = x; i; i = son[i])
pos[i] = ++cnt;
}
for(int i = h[x]; i; i = e[i].next)
split(e[i].v);
}
对于u,v两点,求解时,我们不停将深度大的点向上走(利用top[]),并维护询问,直到在同一链上为止。