这是一篇有关主席树的总结
主席树是什么?
对于原序列的每一个前缀[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