【算法笔记】可持久化线段树
可持久化线段树
可持久化线段树 ,顾名思义,就是可以保留每一个历史版本,并且支持在历史版本上进行操作的线段树。
可持久化线段树
为什么要可持久化呢?有的时候离线维护扫描线之类的东西时,就需要在时间轴里穿梭,这就需要历史版本;权值线段树如果能可持久化,就可以维护区间的数据,达到静态树套树的效果。
那么如何可持久化呢?
首先,最暴力的做法就是,开个线段树,但是这样肯定会爆空间,所以,我们得想点别的招。
如图,这是一个普通的线段树。
我们把第7个数加上3,如图。
仔细观察,就会发现,被修改的节点实际上只是一条链,长度为。
于是,著名神犇hjt突发奇想,如果每次修改只维护一条链的话,空间复杂度就变成了呀。
于是就有了可持久化线段树,也叫主席树(能猜到原因吧)
如图,在可持久化线段树里给第7个数加上3。
从这个图中,我们可以看出可持久化线段树的诀窍在于——复用历史版本的节点。
可持久化线段树只会增加需要修改的节点,而不需要修改的节点就可以使用以前的结构,这种思想被称为“函数式编程“,所以可持久化线段树也叫”函数式线段树“。
核心代码
1 |
|
例题6:可持久化数组
维护这样的一个长度为 的数组,支持如下几种操作:
- 在某个历史版本上修改某一个位置上的值
- 访问某个历史版本上的某一位置的值
此外,每进行一次操作(对于操作,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从开始编号,版本表示初始状态数组)
题目分析
很明显,这一个可持久化线段树模板题,需要单点修改,单点查询,套用模板即可。
AC代码
1 |
|
例题7:静态区间第k小
给定 个整数构成的序列 ,将对于指定的闭区间 查询其区间内的第 小值。
题目分析
如果没有区间操作,查询第k小可以用权值线段树实现,如果有要支持区间操作呢?
我们建一颗可持久化权值线段树,如图,把这个数列的数依次插入。
仔细观察,就会发现第棵树保存着前个数的信息(设初始化的树为第棵)
也就是说,这个可持久化线段树可以说是数列的“前缀树”。
你能想到什么?
可持久化线段树满足区间可加减性,所以我们可以用前缀和的方式找出维护个数的信息的树。
也就是拿出第棵树和第棵树,两者相减,结果就是的信息。
而在相减后的树上找第k小相信大家都已经会了。
那么就可以写出代码了!
注:这题数据很水,题面给,但实际上的数据范围是,甚至不需要离散化的优化,就可以过。
AC代码
1 |
|
实际上,这份代码在除了洛谷以外的其它OJ上是AC不了的,因为题面上的数据范围使代码必须要有离散化的优化,这里给出优化代码。
1 |
|
习题
- 洛谷P3402 可持久化并查集
注意并查集的合并操作 - [POI2014] KUR-Couriers
维护区间绝对众数,有乱搞做法