目录
线段树
模型介绍
通过前面的学习我们都对线段树有了一定的了解。线段树是用于解决区间最值问题和区间求和类问题的树形数据结构,其特点为:支持多次修改、多次查询且时间复杂度都接近o(logn)。下面,假如我们想对数列arr{8,4,5,3}的每个区间求最小值,我们可以建立如下图的线段树。

不难看出,线段树建树是不断二分当前区间并形成新的子节点,同时通过子节点最小值回溯计算父节点最小值的过程。现在,假如我们将arr[3](假定下标从1开始计数)的值修改为2,那么线段树将会变为如下图所示。

接下来,我们再将arr[1]的值修改为1,线段树最终变为如下图所示。

线段树的缺陷
我们将每一次变换看作是一次version(版本)的改变,那么线段树依次经历了三个版本。可以看出,朴素的线段树改值的方式是将原值覆盖,但这样同样会出现一个问题:假如我们想要获得上一个版本中的区间最值呢?换句话说,假如我们对线段树进行了一次操作,我们无法回撤这一次的操作而获取原本的值。
线段树的分类
朴素线段树大致分为两类:普通线段树和权值线段树。对于普通线段树来说,每个节点的区间分别对应了元素在数组中的下标,节点中一般需要维护区间内的最值或区间和;而权值线段树的节点区间则对应了数组的值域,同时维护每个值域内数的出现频次。
主席树(可持久化线段树)
模型介绍
显然,为了解决上述问题,我们需要多个根节点去对应存储不同版本下的线段树。如果我们要经过m次操作获得m个新版本的线段树,那么最简单的方法无疑是创建m棵线段树去对应存储每一个版本了。但是,每棵线段树都需要4n的空间,那么总共的空间复杂度需要o(4mn),这显然已经超出MLE的警戒线了,所以这必然不是一个很好的方案。
我们不妨观察上文中线段树的两次单点插入操作,可以发现,虽然每次操作都会形成一个新版本的线段树,但是实际修改的节点只有一条链式结构,且最多只有logn+1个结点会被修改。于是,我们就产生了这样一种想法:既然其他的结点都不会被修改,那我们是否可以在新版本的线段树下继承这些不需要修改的旧结点,再另外开辟新的结点去存储需要修改的结点呢?
于是,主席树便由此诞生了。接下来,我们仍然以刚才的数据为例,演示主席树的结构。
现在,我们修改arr[3]的值为2,因此可能被修改的结点有[3,3]对应结点、[3,4]对应结点和[1,4]对应结点。因此,我们需要开辟出三个新结点去存储这三个区间的新值。其他结点都不需要更改,因此我们只需要将新结点连接到旧树上,就形成了一个新的线段树,而这个线段树也同样对应了新的版本。

同样地,我们将arr[1]的值修改为1,[1,1]对应结点、[1,2]对应结点和[1,4]对应结点可能被修改,按照同样的方式,我们可以建立如下的新树。

这样,我们便通过一个数据结构建立起了对应多个版本的线段树,我们将它称为可持久化线段树,也就是主席树。
空间复杂度分析
设数组大小为n,共进行m次改值操作,建立初代线段树需要2n-1个节点,每次改值需要新建logn+1个节点,空间复杂度约为o(2n+m(logn+1)),即o(nlogn+3n)级的空间复杂度。
模板题一:洛谷P3919
原题呈现
[题目描述]
如题,你需要维护这样的一个长度为 N 的数组,支持如下几种操作
1. 在某个历史版本上修改某一个位置上的值
2. 访问某个历史版本上的某一位置的值
此外,每进行一次操作(*对于操作2,即为生成一个完全一样的版本,不作任何改动*),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)
[输入格式]
输入的第一行包含两个正整数 N, M , 分别表示数组的长度和操作的个数。
第二行包含 N 个整数,依次为初始状态下数组各位的值(依次为 ai, 1 ≤ i ≤ N)。
接下来 M 行每行包含3或4个整数,代表两种操作之一( i 为基于的历史版本号):
1. 对于操作1,格式为 vi 1 loci valuei,即为在版本 vi 的基础上,将 aloci 修改为 valuei
2. 对于操作2,格式为 vi 2 loci,即访问版本 vi 中的 aloci 的值,生成一样版本的对象应为vi
[输出格式]
输出包含若干行,依次为每个操作2的结果。
[输入样例]
5 10
59 46 14 87 41
0 2 1
0 1 1 14
0 1 1 57
0 1 1 88
4 2 4
0 2 5
0 2 4
4 2 1
2 2 2
1 1 5 91
[输出样例]
59
87
41
87
88
46
[数据规模]
对于30%的数据: 1 ≤ N, M ≤ 1e3
对于50%的数据: 1 ≤ N, M ≤ 1e4
对于70%的数据: 1 ≤ N, M ≤ 1e5
对于100%的数据: 1 ≤ N, M ≤ 1e6, 1 ≤ loci ≤ N, 0 ≤ vi < i, -1e9 ≤ ai, valuei&nbs

本文详细介绍了线段树及其在区间最值和求和问题中的应用,讨论了线段树的缺陷,即无法回溯历史版本。然后引出了可持久化线段树,即主席树,通过动态开点和双指针同步搜索解决了版本回溯的问题。文章提供了两个模板题,分别展示了主席树在单点修改和区间第k小值查询中的实现,以及空间复杂度和时间复杂度分析。
最低0.47元/天 解锁文章





