主席树学习笔记
主席树学习笔记
前置芝士
- 线段树
- 动态开店线段树
- 权值线段树
- 脑子
pvz 玩多了
用处
主席树即可持久化线段树,在线段树的基础上可以查询修改每个修改的内容并形成新的版本。
思想
如果每次修改后在建一棵线段树,空间复杂度肯定受不了。注意到线段树的一次修改操作最多影响 $\log$ 个节点。因此我们可以只新建修改的节点,将原来的节点也用上,这样空间复杂度就是 $\Theta((n + q) \log n)$ 的。
实现
给出一个长度为 $n$ 的序列 $a_i$,此时的序列为版本 $0$ ,支持一下两种操作;
在一个版本的基础上单点修改,形成一个新的版本;
在一个版本的基础上单点查询,形成一个一模一样的新版本。
$1 \le n,q \le 10^6$。
板子题(当然其它方法也很多),就来看看实现。需要动态开点,所以要记录所有儿子的信息:
1 |
|
初始建树、查询和普通线段树没什么区别,我们来看看修改操作:
1 |
|
完整代码
1 |
|
但是我听机房巨佬说,这道板子题是没有灵魂的 /jk ,所以我们在来一道有灵魂的:
给定一个序列,每次查询一段区间的第 $k$ 小值。
$1 \le n,q \le 2 \times 10^5$,$1 \le a_i \le 10^9$。
巨佬说这个就很有灵魂了。我们建立一棵权值线段树,记录维护区间的数的数量。遍历离散化后的序列,每次在前面插入一个数形成新版本(有点像前缀和),接下来我们考虑查询。
首先考虑查询的区间 $\left[1,x\right]$,在 $x$ 这个点对应版本的线段树上,我们进行查询。怎么查很简单,如果左边的数量小于 $k$ 就进入右子树,否则查左子树。那查询 $\left[l,r\right]$ 就很简单了,还是前缀和的思想,在做查询时同步遍历 $l - 1$ 和 $r$ 两棵线段树,相减就得到了 $\left[l,r\right]$ 区间的信息注入灵魂。
妙啊!
CODE
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!