主席树
主席树的主体是线段树,准确的说,是很多棵线段树,存的是一段数字区间出现次数(所以要先离散化可能出现的数字)。举个例子,假设我每次都要求整个序列内的第 k 小,那么对整个序列构造一个线段树,然后在线段树上不断找第 k 小在当前数字区间的左半部分还是右半部分。这个操作和平衡树的 Rank 操作一样,只是这里将离散的数字搞成了连续的数字。
先假设没有修改操作:
对于每个前缀 S1…i,保存这样一个线段树 Ti,组成主席树。这样是会 MLE 的,所以我们用到了一点优化。
注意,这个线段树对一条线段,保存的是这个数字区间的出现次数,所以是可以互相加减的!还有,由于每棵线段树都要保存同样的数字,所以它们的大小、形态也都是一样的!这实在是两个非常好的性质,是平衡树所不具备的。
对于询问 (i,j),我只要拿出 Tj 和 Ti-1,对每个节点相减就可以了。说的通俗一点,询问 i..j 区间中,一个数字区间的出现次数时,就是这些数字在 Tj 中出现的次数减去在 Ti-1 中出现的次数。
那么有修改操作怎么办呢?
如果将询问看成求一段序列的数字和,那么上面那个相当于求出了前缀和。加入修改操作后,就要用树状数组等来维护前缀和了。于是那个 “很好的性质” 又一次发挥了作用,由于主席树可以互相加减,所以可以用树状数组来套上它。做法和维护前缀和长得基本一样.
主席树中有一些性质方便我们进行运算
①主席树的每个结点,保存的是这个区间含有的数字的个数。
②主席树的每个结点,也就是每颗线段树的大小和形态也是一样的,也就是主席树之间可以相互进行加减运算。
……………………………………………………………………………………………………………………………………………………
然后还是看看这道模板题吧
题目背景
这是个非常经典的主席树入门题——静态区间第K小
题目描述
如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。
输入格式:
第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。
第二行包含N个正整数,表示这个序列各项的数字。
接下来M行每行包含三个整数 l, r, kl,r,k , 表示查询区间[l, r][l,r]内的第k小值。
输出格式:
输出包含k行,每行1个正整数,依次表示每一次查询的结果
输入样例:
5 5
25957 6405 15770 26287 26465
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1
输出样例
6405
15770
26287
25957
26287
数据范围:
对于20%的数据满足:1≤N,M≤10
对于50%的数据满足: 1≤N,M≤103
对于80%的数据满足: