重链剖分(维护树上信息)复习笔记

本文将介绍重链剖分算法。

问题的引入

看下面一道题:

有一棵根为 1 的树,点上有点权,进行如下操作:

  1. 将以 x 为根的子树内所有节点的点权增加 k
  2. 求以 x 为根的子树内所有节点的点权之和。

节点数 1n105,询问数 1m105

显然,本题可以简单地使用 dfs 序线段树,将树上问题转化为数列上问题。

那么如果添加两个新的操作呢?

  1. xy 的简单路径上所有节点的点权增加 k
  2. xy 的简单路径上所有节点的点权之和。

以上问题便是 洛谷 P3384 轻重链剖分

此时 dfs 序线段树便无法解决了,需要引入一个新的算法,也就是重链剖分。

重链剖分

概念和定义

以下是重链剖分中的一些定义:

  • 重儿子:对于一个非叶子节点,它的儿子中子树最大的那个节点称为它的重儿子。
  • 轻儿子:对于一个非叶子节点,它的所有儿子中不是重儿子的都称为它的轻儿子。
  • 重边:一个非叶子节点与它的重儿子相连的边称为重边。
  • 重链:由一系列重边相连组成的链称为重链。
  • 轻链:由一系列非重边相连组成的链称为轻链。

根据以上定义,显然有:

  • 叶子节点没有重儿子或轻儿子(因为它没有儿子)。
  • 每一条重链的起点是轻儿子。
  • 所有重链互不相交,每个点只属于一条重链。
  • 重链长度之和为 O(n)(因为每个点只属于一条重链)。
  • 一个点到根节点经过的轻边条数最多为 O(logn)

我们定义的数组如下:

  • disuu 的深度。
  • fauu 的父亲。
  • szuu 的子树大小。
  • sonuu 的重儿子编号。
  • topuu 所在重链的顶端。
  • dfnuu 的 dfs 序(注意这里的 dfs 序与普通的不同,将在 dfs2 进行讲解。)

dfs1

在第一次 dfs 中,我们需要处理出每个节点的 dis,fa,sz,son

伪代码:

1Method dfs1(u,f).2let disudisf+13let fauf4let szu15for v{son of u}6dfs1(v,u)7if szv>szsonu8let sonuv

dfs2

在第二次 dfs 中,我们需要处理出每个节点的 top,dfn,要先 dfs2 处理重儿子再处理轻儿子。

伪代码:

1Method dfs2(u,tp).2let toputp3let tottot+14let dfnutot5if u is leaf6return7dfs2(sonu,tp)8for v{son of uvsonu}9dfs2(v,v)

数据处理

我们可以在 dfs2 中处理出在 dfn 意义下每个点的权值,然后使用线段树维护即可。

对于链上操作,我们每次找到深度较深的点,因为 dfs2 中先处理重链后处理轻链,所以重链的 dfn 是连续的,直接将这个点到重链顶端的区间进行操作,然后将这个点跳到重链顶端的父亲。

一直重复如上操作,直到两个点在同一条重链,此时两个点之间的 dfn 也是连续的,再进行一次区间操作即可。

伪代码:

1Method modify(u,v).2while toputopv3if disu<disv4swap u,v5SegmentTreeModify([dfntopu,dfnu])6let ufatopu7if disu<disv8swap u,v9SegmentTreeModify([dfnv,dfnu])

本题完结撒花!

一些习题

posted @   rui_er  阅读(71)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示