0.前置知识
必备:链式前向星,dfs序。
维护:线段树、树状数组、BST。
1.引入
树链剖分,简单来说就是把树分割成链,然后维护每一条链。一般的维护算法有线段树,树状数组和BST。复杂度为。
2.剖分方式
对于每一个节点,它的子节点中子树的节点数最大的为重儿子,连接到重儿子的边称为重边。例如:
加粗节点为重儿子。除重儿子和重边外的节点和边均为轻儿子或轻边。根不是重儿子也不是轻儿子,因为它根本就不是儿子。
以轻儿子或者根为起点的,由重边连接的一条连续的链称为重链。特别地,若一个叶子结点是轻儿子,那么便有一条以该叶子结点为起点的长度为1的重链。上图中,是一条重链,一个节点也是一条重链。
3.预处理
树链剖分的预处理本质上就是2个dfs。
第一个dfs
一共完成四项任务。
- 标记节点深度:dep[]
- 标记节点的父节点:fa[]
- 标记节点的子树大小:siz[]
- 标记节点的重儿子编号:son[]
代码:
void dfs_1(int u, int f) {
siz[u] = 1;
for (int i = h[u]; i; i = e[i].next) {
int v = e[i].v;
if (v == f)continue;
fa[v] = u;
dep[v] = dep[u] + 1;
dfs_1(v, u);
siz[u] += siz[v];
if (siz[v] > siz[son[u]])son[u] = v;
}
}
第二个dfs
完成三项任务。
- 标记dfs序(先重后轻):idx[]
- 标记dfs序对应的节点:rnk[]
- 标记节点所在重链的顶端:top[]
代码:
void dfs_2(int u, int tp) {
idx[u] = ++dfn;
rnk[dfn] = u;
top[u] = tp;
if (son[u])dfs_2(son[u], tp);
for (int i = h[u]; i; i = e[i].next) {
int v = e[i].v;
if (fa[u] == v || son[u] == v)continue;
dfs_2(v, v);
}
}
来源:CSDN
作者:alexand_er
链接:https://blog.csdn.net/alexand_er/article/details/103743809