树链剖分

安稳与你 提交于 2019-12-28 17:16:20

0.前置知识

必备:链式前向星,dfs序。
维护:线段树、树状数组、BST。

1.引入

树链剖分,简单来说就是把树分割成链,然后维护每一条链。一般的维护算法有线段树,树状数组和BST。复杂度为O(logn)O(log n)

2.剖分方式

对于每一个节点,它的子节点中子树的节点数最大的为重儿子,连接到重儿子的边称为重边。例如:

加粗节点为重儿子。除重儿子和重边外的节点和边均为轻儿子或轻边。根不是重儿子也不是轻儿子,因为它根本就不是儿子。
以轻儿子或者根为起点的,由重边连接的一条连续的链称为重链。特别地,若一个叶子结点是轻儿子,那么便有一条以该叶子结点为起点的长度为1的重链。上图中,13681-3-6-8是一条重链,77一个节点也是一条重链。

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);
	}
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!