一些概念
在学习重链剖分前,首先要明白以下几个概念:
中二重儿子:就是一个节点的儿子中最“重”的那个,“重”表示的是子树大小最大,如果都一样大,就随便选一个就好了(用 s o n son son数组存储)亲轻儿子:除了重儿子外其他的儿子- 重边:重儿子和父亲之间的边
- 轻边:轻儿子和父亲之间的边
- 重链:重边连在一起形成的链
- 轻链:轻边连在一起形成的链(貌似没啥用)
- 重链顶点:一条重链中,深度最小的点(用 t o p top top数组记录)
为了方便大家理解,这里我画了一张图,来表示重链剖分后的结果:
其中,红色的线表示重边,连在一起,形成 3 3 3条重链;黑色的线表示轻边,连在一起,形成 3 3 3条轻链
这样,大家对这些概念应该都了解了吧?
算法讲解
接下来,我们来介绍如何进行重链剖分
首先,我们进行第一遍dfs,求出子树的大小( s i z siz siz),重儿子( s o n son son),父亲节点( f a fa fa)和每个点的深度( d e p dep dep)
s i z e size size、 f a fa fa和 d e p dep dep的求法就不用说了吧……
我们来讲讲 s o n son son的求法
首先,设一个变量 m a x s o n maxson maxson,初值为 − 1 -1 −1,记录最大的子树大小
接着,对于枚举的每棵子树,假设当前枚举的子树的根为 v v v,判断 m a x s o n maxson maxson和 s i z [ v ] siz[v] siz[v]的大小
如果 s i z [ v ] > m a x s o n siz[v]>maxson siz[v]>maxson,那么 s o n [ u ] = v son[u]=v son[u]=v
代码:
int dfs1(int x,int Fa,int Dep)
{
dep[x]=Dep,fa[x]=Fa,siz[x]=1;
int maxson=-1;
for(int i=head[x];i!=-1;i=e[i].nxt) if(e[i].v!=Fa){
siz[x]+=dfs1(e[i].v,x,Dep+1);
if(siz[e[i].v]>maxson) maxson=siz[e[i].v],son[x]=e[i].v;
}
return siz[x];
}
接着,我们来讲一讲第二个dfs,这个dfs要求出dfs序( d f n dfn dfn)和重链顶点( t o