主席树乱讲

主席树乱讲

前置技能

  • 线段树:动态开点,标记永久化,基本操作
  • 离散化

介绍

主席树即可持久化线段树,也叫作函数式线段树
至于为什么叫做主席树,据说是一个叫HJT的神犇在考场上现场yy出来的
可持久化线段树:
顾名思义就是线段树经过了若干次修改之后,仍然能找到原来某次修改前的线段树的信息的一种数据结构

建立

最暴力的方法就是,每次修改就复制当前的线段树新建下一版本的一棵树,在上面修改
空间复杂度 O(mnlogn) O ( m n l o g n ) ,时间复杂度 O(mnlogn) O ( m n l o g n )
这样并不高效,还不如不用

进行优化

  • 考虑单点修改
    线段树单点修改只会修改一条链,即 log l o g 个点,其它的点不变
    那么我们为什么不可以把其它不变的点继续利用?
    所以我们就可以只新建这条链的点其它的点直接接上去就行了
    那么空间复杂度就是 O(nlogn) O ( n l o g n ) ,时间复杂度 O(nlogn) O ( n l o g n )
  • 考虑区间修改
    如果像单点修改那样,新建改变的节点
    那么区间标记不好进行 pushdown p u s h d o w n
    标记永久化,这个常数小,空间小,又快又方便
    这个好,加上标记永久化就解决了

记得要用动态开点线段树

单点修改模板题Luogu3919
区间修改模板题SPOJ/Vjudge


用法

不要尝试用整体二分等离线算法水过去强制在线

1. 区间第 k k 小(大)问题

Luogu静态区间第k小模板题
给定N个正整数构成的序列, M M 组询问,对于指定的闭区间查询其区间内的第K小值

N,M<=2105 N , M <= 2 ∗ 10 5

做法

区间第 k k 小这一类算是比较套路了
考虑如果查全局第k
可以开一棵值域线段树,维护数的个数
把数字离散化之后丢进线段树中去
然后直接在线段树上二分数字
如果 k k 大于当前点左子树的点的个数,那么k减去这个个数,去右子树
否则去左子树

这道题的做法:
类似
把数字离散化,主席树为值域线段树,维护数的个数
从第一个数字开始不停的往主席树中加点,每加一个数字,就新建一个版本
那么第 i i 棵线段树保存的就是前i个数字
查询区间 [l,r] [ l , r ] k k 小?
和全局第k小的做法一致,但是我们要找的只是数组 [l,r] [ l , r ] 中的数
之前说过第 i i 棵线段树保存的就是前i个数字
那么运用前缀和的方法,把第 r r 棵线段树和第l1棵线段树作差,得到的就是这个区间内的数,其它的都和全局第 k k 小没有区别

此时树就像是一个二维平面
横纵坐标分别为树的版本和每一棵树

提一下:如果要做动态的区间第k小,即带修改,要用到树状数组套线段树来做,思路很相似,就是把数组区间那一维用树状数组来维护

2. 树上路径第 k k 小(大)

Bzoj2588Count on a tree
一棵N个节点的树,每个点有一个权值,对于 M M 个询问(u,v,k),你需要回答 u u v这两个节点间第 K K 小的点权

N,M<=100000

做法
主席树还是为值域线段树,维护数的个数
每个点的线段树版本由它的父亲加入它的点权得到
那么每个点的线段树存的就是它到根的所有点的点权
还是树上二分数字,还是线段树作差:
假设 u,v u , v 的LCA为 x x ,用rt[i]表示 i i 这个点的线段树
那么就是这样作差
rt[u]+rt[v]rt[x]rt[fa[x]]
这样就只包含了 u,v u , v 路径上所有的点了

3. 区间中位数

HDU4251
给定 N N 个正整数构成的序列A,将对于指定的闭区间查询其区间内的中位数的值

N,M<=100000,A[i][1,109] N , M <= 100000 , A [ i ] ∈ [ 1 , 10 9 ]

做法
emmm... e m m m . . .
这不就是区间第长度除以 2 2 小吗?

4. 区间不重复的数的个数

SPOJ3267
给定N个正整数构成的序列 A A Q组询问,于指定的闭区间查询其区间内的不同的数的个数

N,Q<=30000,A[i][1,106] N , Q <= 30000 , A [ i ] ∈ [ 1 , 10 6 ]

做法
不是权值线段树
维护位置
如果插入一个数时发现之前有过了
那么修改当前的,那个位置 1 − 1
然后插入这个数字,在相应的位置 +1 + 1
询问 [l,r] [ l , r ] 就是第 r r 棵线段树中[l,r]的区间和

5. 区间前 k k 小(大)的和

好像没有找到题诶。。。
给定N个正整数构成的序列,将对于指定的闭区间查询其区间内前 k k 小的数的和
N,M<=100000

做法
先求第 k k <script type="math/tex" id="MathJax-Element-60">k</script>小,答案就是它和它子树的点的值的和
也就是主席树再存下每个点离散化的值,维护值的和就好了

一些其它的就不列举了

主要是没什么了,而且其它的基本上方法差不多

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值