这是一篇有关主席树的总结
主席树是什么?
对于原序列的每一个前缀[1···i]建立出一棵线段树维护值域上每个数出现的次数,则其树是可减的
PS:本篇随笔对于主席树的基本内容并没有深刻讲解,主要说明它的一些用法
其实就是很多一堆大量的权值线段树
(什么是权值线段树?就是每个节点维护不是位置,而是权值,比如 \([1,4]\) 维护的就是权值等于1到权值等于4的信息)
而且这些线段树还隐含了一个前缀和的功能
最简单的就是一个数列,长度为 \(n\) ,那么就建 \(n\) 棵权值线段树,第 \(i\) 棵权值线段树对应第 \(i\) 个数,存的是从第一个数到第 \(i\) 个数的信息(建第 \(i\) 棵权值线段树的时候,只要先把第 \(i-1\) 棵权值线段树复制一份,再把 \(i\) 位置上的信息加入中间就可以了)
那么我们如果要知道数列中 \(l\) 位置到 \(r\) 位置的数的信息,就只要把 \(r\) 上的权值线段树减去 \(l-1\) 的权值线段树,得到的线段树保存的就是 \(l\) 到 \(r\) 的信息(其实就是一个差分)
然后我们发现每次都新建整个权值线段树又耗时间又爆内存,并且相邻两棵线段树之间改变的只有一点点内容,其它的都没有变,那对于这些不变的节点就不新建了,直接共用,即多棵权值线段树在某个位置共用一个节点
个人认为,主席树就是共用节点的权值线段树
主席树可以用来干嘛?
主席树的所有操作,基本上都可以说是对应一段区间,那么就都要先差分一下,以下所有功能都是差分之后的
- 查询区间Kth(第 \(k\) 大/小)
主席树中记录的是 \(num\) ,即每个数字出现的次数,然后每次判断当前区间的左子树的 \(num\) 是否满足条件,再决定走左子树还是右子树(极其类似于Treap) - 查询区间出现数字的和
这只要把上面的 \(num\) 变成 \(val\) ,然后把差分后根节点的 \(val\) 输出就行了 - 查询区间中权值在 \([l,r]\) 之间的所有数字的和
把 \(num\) 变成了 \(val\) 后,不直接返回根节点的值,而是返回主席树上 \([l,r]\) 区间的 \(val\) 值(因为主席树是权值线段树)
暂知的作用就这几个
几个小套路
- 如果没有修改只有询问操作,那么可以直接静态主席树。但如果有修改操作,那么就要变成动态了。也就是说我们要有一个东西,可以维护能够修改的前缀和,那就BIT(树状数组)。所以动态主席树,一般就是树状数组套主席树的树套树
- 如果不是一个数列,而是一棵树上要维护两个节点的信息,那么就把主席树的每个版本变成从父亲继承的就行了,最后的查询就树上差分
这些套路要在题目之中去理解
几道练习题
大体上从简到难(看个人理解)
- 【刷题】洛谷 P3834 【模板】可持久化线段树 1(主席树)(最基本的板子,入门题)
- 【刷题】BZOJ 3524 [Poi2014]Couriers(询问的内容稍微改变,但本质上没有区别,维护的东西也没有变)
- 【刷题】BZOJ 2588 Spoj 10628. Count on a tree(这就是树上的维护了,每个版本要从父亲继承)
- 【刷题】洛谷 P3573 [POI2014]RAJ-Rally(与主席树无关,但与权值线段树有关,神题一道)
- 【刷题】BZOJ 1901 Zju2112 Dynamic Rankings(第一道树套树,动态Kth)
- 【刷题】BZOJ 1926 [Sdoi2010]粟粟的书架(一道二合一题,其中有主席树,算是一个应用,二分+主席树)
- 【刷题】BZOJ 3295 [Cqoi2011]动态逆序对(树套树的应用)
七道题,后面的神题做得心累
没有数论题好做啊
从未结束
从此,主席树的学习告一段落
HNOI就要到了,可是HNOI最喜欢考的数据结构我还有很多没搞定
下一步——LCT