主席树
可持久化线段树(主席树)
对于原序列的每一个前缀[1…i]建立出一颗线段树维护值域,建立一棵线段树维护值域上每个数出现的次数,则其树是可减的
—-黄嘉泰
如果对线段树不了解的,请学习前置技能:线段树]
值域线段树
对一个值域上的个数进行维护的线段树
举个例子:1,1,2,3,3,3,4,其值域 线段树为:
主席树
hjt树便是基于权值线段树的基础而建立的
经典例题:求区间上的第k大树
步骤1.先对数据进行离散化(此步骤不是重点,因此省略)
步骤2:如何处理区间[L,R]这个条件呢?在考虑右侧 ,R] 这个限定的时候,我们只需要寻找第R个版本即可。
那么,怎么处理 [L, 这个限定呢?
这也是这道题目最关键的:我们在对第R个版本进行递归的查找的时候,我们会先搜左子树,如果k大于左子树的数量我们才会在右子树上搜索。那么左子树的数量就显得尤为关键了。
回顾hjt那句话,因为主席树维护的是权值线段树,所以我们在递归第R个版本的时候,实际上是递归的除去第L-1个版本的所有内容,因此我们只需利用权值线段树的性质,减去L-1版本的内容就好啦👲
下面是算法拆分:
树的存储
1 | //使用结构体存储主席树,其中l,r分别表示指向左右儿子的指针 |
建树
1 | //build()建树 |
树的插入
1 | //insert into thr tree |
树的查询
1 | //find the kth num in the segment of [l,r] |
板子题:洛谷P3834 https://www.luogu.com.cn/problem/P3834
1 |
|
主席树维护普通线段树:可持久化数组
可持久化数组是用主席树维护普通线段树,是学习可持久化并查集的基础
- 可持久化数组支持历史版本的单点修改和单点查询
下面是整个算法的步骤:
- 结构
1 | struct Node{ |
- 建树
1 | void build(int &now,int l,int r){ |
- 更新节点
1 | void modify(int l,int r,int &now,int pre,int pos,int x){ //搜索[l,r],依据pre版本对now版本找到pos位置修改成x |
- 查询
1 | int query(int l,int r,int now,int pos){ |
板子题:洛谷P3919 https://www.luogu.com.cn/problem/P3919
1 |
|