主席树乱讲
前置技能
- 线段树:动态开点,标记永久化,基本操作
- 离散化
介绍
主席树即可持久化线段树,也叫作函数式线段树
至于为什么叫做主席树,据说是一个叫HJT的神犇在考场上现场yy出来的
可持久化线段树:
顾名思义就是线段树经过了若干次修改之后,仍然能找到原来某次修改前的线段树的信息的一种数据结构
建立
最暴力的方法就是,每次修改就复制当前的线段树新建下一版本的一棵树,在上面修改
空间复杂度\\(O(mnlogn)\\),时间复杂度\\(O(mnlogn)\\)
这样并不高效,还不如不用
进行优化
- 考虑单点修改
线段树单点修改只会修改一条链,即\\(log\\)个点,其它的点不变
那么我们为什么不可以把其它不变的点继续利用?
所以我们就可以只新建这条链的点其它的点直接接上去就行了
那么空间复杂度就是\\(O(nlogn)\\),时间复杂度\\(O(nlogn)\\) - 考虑区间修改
如果像单点修改那样,新建改变的节点
那么区间标记不好进行\\(pushdown\\)
标记永久化,这个常数小,空间小,又快又方便
这个好,加上标记永久化就解决了
记得要用动态开点线段树
单点修改模板题Luogu3919
区间修改模板题SPOJ/Vjudge
用法
不要尝试用整体二分等离线算法水过去强制在线
1. 区间第\\(k\\)小(大)问题
Luogu静态区间第k小模板题
给定\\(N\\)个正整数构成的序列,\\(M\\)组询问,对于指定的闭区间查询其区间内的第\\(K\\)小值
\\(N,M<=2*10^5\\)
做法
区间第\\(k\\)小这一类算是比较套路了
考虑如果查全局第\\(k\\)小
可以开一棵值域线段树,维护数的个数
把数字离散化之后丢进线段树中去
然后直接在线段树上二分数字
如果\\(k\\)大于当前点左子树的点的个数,那么\\(k\\)减去这个个数,去右子树
否则去左子树
这道题的做法:
类似
把数字离散化,主席树为值域线段树,维护数的个数
从第一个数字开始不停的往主席树中加点,每加一个数字,就新建一个版本
那么第\\(i\\)棵线段树保存的就是前\\(i\\)个数字
查询区间\\([l,r]\\)第\\(k\\)小?
和全局第\\(k\\)小的做法一致,但是我们要找的只是数组\\([l,r]\\)中的数
之前说过第\\(i\\)棵线段树保存的就是前\\(i\\)个数字
那么运用前缀和的方法,把第\\(r\\)棵线段树和第\\(l-1\\)棵线段树作差,得到的就是这个区间内的数,其它的都和全局第\\(k\\)小没有区别
此时树就像是一个二维平面
横纵坐标分别为树的版本和每一棵树
提一下:如果要做动态的区间第\\(k\\)小,即带修改,要用到树状数组套线段树来做,思路很相似,就是把数组区间那一维用树状数组来维护
2. 树上路径第\\(k\\)小(大)
Bzoj2588Count on a tree
一棵\\(N\\)个节点的树,每个点有一个权值,对于\\(M\\)个询问\\((u,v,k)\\),你需要回答\\(u\\)和\\(v\\)这两个节点间第\\(K\\)小的点权
\\(N,M<=100000\\)
做法
主席树还是为值域线段树,维护数的个数
每个点的线段树版本由它的父亲加入它的点权得到
那么每个点的线段树存的就是它到根的所有点的点权
还是树上二分数字,还是线段树作差:
假设\\(u, v\\)的LCA为\\(x\\),用\\(rt[i]\\)表示\\(i\\)这个点的线段树
那么就是这样作差
\\(rt[u]+rt[v]-rt[x]-rt[fa[x]]\\)
这样就只包含了\\(u,v\\)路径上所有的点了
3. 区间中位数
HDU4251
给定\\(N\\)个正整数构成的序列\\(A\\),将对于指定的闭区间查询其区间内的中位数的值
\\(N,M<=100000,A[i] \\in [1, 10^9]\\)
做法
\\(emmm...\\)
这不就是区间第长度除以\\(2\\)小吗?
4. 区间不重复的数的个数
SPOJ3267
给定\\(N\\)个正整数构成的序列\\(A\\),\\(Q\\)组询问,于指定的闭区间查询其区间内的不同的数的个数
\\(N,Q<=30000,A[i]\\in[1, 10^6]\\)
做法
不是权值线段树
维护位置
如果插入一个数时发现之前有过了
那么修改当前的,那个位置\\(−1\\)
然后插入这个数字,在相应的位置\\(+1\\)
询问\\([l,r]\\)就是第\\(r\\)棵线段树中\\([l,r]\\)的区间和
5. 区间前\\(k\\)小(大)的和
好像没有找到题诶。。。
给定\\(N\\)个正整数构成的序列,将对于指定的闭区间查询其区间内前\\(k\\)小的数的和
\\(N,M<=100000\\)
做法
先求第\\(k\\)小,答案就是它和它子树的点的值的和
也就是主席树再存下每个点离散化的值,维护值的和就好了
一些其它的就不列举了
主要是没什么了,而且其它的基本上方法差不多