主席树可以解决不适用结合律的区间问题(如区间第 $K$ 大,区间种类数),这些问题原本是需要繁琐的树套树,而有了主席树就简单很多了。
主席树的中心思想是保留历史版本,最暴力的做法是没插入一个节点就新建一棵线段树,但这样会各种爆,其实我们可以只新建有更改的节点,然后直接连边到原来的节点即可。
类比普通的线段树,主席树的插入顺序相当于普通线段树的位置,而主席树中的位置是维护的权值。
例题
[POJ2104] K-th Number
给定一个长度为 $N$ 的序列 $a$ ,有 $M$ 次查询,每次查询区间 $[l,r]$ 中第 $K$ 大的数值。
$n\leq10^5,m\leq5000,a_i\leq10^9$
离散化之后用主席树维护
1 |
|
[BZOJ1878] HH 的项链
给定一个长度为 $N$ 的序列 $a$,共有 $M$ 个询问,对每个询问 $[l,r]$ ,需要回答 $[l,r]$ 之间的种类数。
$N\leq5\times10^4,M\leq2\times10^5,0\leq a_i\leq10^6$
我们对于每个位置记录一下加一个与它相同的数的位置 $nextPos[i]$, 这样,我们用主席树维护 $nextPos$ 数组,对于每个查询只需要统计区间内有多少个 $nextPos[i]>r$ 即可。
1 | /************************************************************** |
[BZOJ1901]动态排名系统
给定一长度为 $N$ 的序列 $a$, 有 $M$ 次操作,每次查询区间 $[l,r]$ 中第 $K$ 大的数值,或单点修改。
$N\leq5\times10^4,M\leq10^4$
本题就是在例一的基础上增加了单点修改操作,如果直接修改每次需要 $O(n\log_2n)$ 的时间复杂度,这是不能接受的。注意到原本的主席树每个节点维护的是前缀信息,可以利用树状数组优化,即将原先的对于每一个新元素的前缀建树,改为对于树状数组中的前缀建树,这样可以将每次修改的时间复杂度降为 $O(\log_2^2n)$
需要注意的是,在修改时不用新建节点,因此插入函数稍有改动。
1 |
|
[BZOJ2120]数颜色
给定一个长度为 $N$ 的序列 $a$,共有 $M$ 个操作,查询需要回答 $[l,r]$ 之间的种类数,或单点修改。
$N\leq10^4,M\leq10^4$, 修改操作不多于 $10^3$ 次,所有的输入数据中出现的所有整数 $1\leq x\leq10^6$。
1 |
|