主席树相关问题

主席树(可持久化线段树)是一种适用范围相当广的数据结构。

其衍生出了可持久化字典树(思想上),可持久化数组、并查集(维护上)

本文是我结合题目的一些应用技巧总结。注意某些题目仅一部分涉及主席树,我会将这一部分单独拿出来

(有些人认为只有可持久化权值线段树为主席树,我这里不进行称谓的区分)


模板题就不再细说了,这里放一道题里方便查板子

 

1.经典应用-静态区间第k大(小)

例题

提到这个题主要是为了说明要注意主席树的下标代表着什么。

有时候是权值,有时候是深度,有时候是dfs序。反正乱七八糟的什么都有,使用时一定要区分好下标、统计的数与继承的方式


 

2.经典扩展-带修区间第k大(小)

例题

由于主席树一旦修改就要修改所有版本,所以这题不能暴力修改。

我们将每个版本的主席树看做一个点,对于这些点建立一个BIT,那么每个点保存的信息就是[i-lowbit(i)+1,i]的信息

我们重点注意这和原来的主席树有什么差异

原来的主席树保存的是[1,i]的所有信息,即有些根节点保存的信息范围被缩小了

那如果暂时不考虑空间的问题,用线段树维护呢

当然我不觉得会有人这么写

那么每个节点实际上维护的是区间[l,r]的信息,一共有nlogn个节点。

比起BIT,这种储存信息的方式肯定会更全,但是完全没有用

因为主席树的使用条件要符合区间可加性(就是前缀和的性质)——我们需要在查询的时候对区间内的信息进行合并(加减运算),而这正好也是BIT的使用条件

所以在此类问题中,BIT可以完全取代线段树(当然你要练代码能力那另说)

 

其实上面的两种主席树做法在效率上来说是劣于整体二分的(甚至可以说是被吊打),但是其思想是需要学习的


 

3.经典应用-减少空间消耗

例题

对于中位数问题,直接二分中位数的大小,对数列进行1、-1的再赋值判断即可

对于此题,将答案拆开为[a,b]、[b-1,c-1]、[c,d]三段,使其和最大

即维护区间和、左最大、右最大,考虑使用线段树

但是线段树的空间过大,而且当mid——>mid+1时,仅会有一个节点的值发生变化,则此时可以考虑用主席树代替线段树。

以值域为下标,建立主席树即可

 

上面的经典应用都是在序列上的,下面的一些扩展就开始上树了


 

4.对于某个点,查询树上某权值最深的节点

本题的倍增+主席树做法中有体现

这个一眼就可以看出来是主席树了,直接以节点编号为下标

只要在dfs时继承父亲的信息进行单点修改即可


 

5.对于某个点,查询子树内距离不超过k的点的数量

本题中有体现

在向下查询子树的时候,直接将主席树上包含x子树的线段树给抠(即rt[dfn[x]]~rt[dfn[x]+size[x]-1])出来,对[dep[x]+1,dep[x]+k]区间查询即可

(dfn表示dfs序,size表示子树大小)

注意此时下标为深度,继承的信息不再是父亲的而是dfs序前一个的

 

思考一下4和5的不同之处

4中我们要求的是1~x的路径上的点,所以可以直接继承父亲的信息。

而5是子树内的点,我们需要将dfs序上连续的一段给抠出来,所以信息的继承必须是连续的。切记不要将两种情况搞混


 

6.求树上x,y两点间权值第k大的点

例题

 将路径拆开为(1,x)、(1,y)、(1,lca(x,y))、(1,fa[lca(x,y)])

用4中的建树方法进行建树即可(其实就是树上的套路处理了一下)

 

上面总结了一下树上的常见问题,由于本人实力问题,后面应该会有所补充

下面的题目是我个人觉得很有意思的一道题


 

7.区间修改,单点查询——类比树状数组

例题

前面我们提到过,主席树和BIT的使用条件是有相似之处的,我们试着从这个角度出发来思考一下这道题。

考虑BIT的区间修改,我们用的是差分的方式,所以我们可以考虑也这么维护主席树,将其转化为单点修改和区间查询

这样这道题就顺利的解决了。


 

结语

总结一下,主席树的问题主要会出在节点编号的含义、信息的继承方式和统计的数的区分上(至少我个人经常有点晕)

对于主席树与BIT的关系♂,两者在使用条件上有一些相似,但是实现的功能却不完全相同,思考时可以进行类比以打开思路(因为树状数组肯定熟啊,会好想一点)

posted @ 2022-05-06 17:50  young_fan  阅读(56)  评论(0)    收藏  举报