【暖*墟】 #数据结构进阶# 主席树超详细解释

主席树【可持久化线段树/函数式线段树/n棵线段树】

         目录

一. 概念理解

1.求所有数字中的第k大数?

2.求任意一段区间 [ L , r ]中的第k大数?

3.会不会空间超限?

超级详细的实现过程分析【戳我戳我qwq】

二. 例题演练

1 【主席树】第K小的数Ⅰ(caioj1441)

2 【主席树】第K小的数Ⅱ(caioj1442)

3 【主席树】第K小的数Ⅲ(caioj1443)

4 【主席树】逆序对数(caioj1444)

5【主席树】去月球(caioj1447)


一. 概念理解

  • 离线数据结构
  • 可以查询区间第k大
  • 复杂度log(n)

1.求所有数字中的第k大数

先将所有数字离散化【排序+去重】(所以是离线)。

对离散化后的数字建立一颗线段树,每个节点【 统计当前范围内的数的个数 】

自顶向下查找,如果左边区间个数大于等于k则在左边,小于则在右边(从大到小排序)。

2.求任意一段区间 [ L , r ]中的第k大数

建立n棵线段树,每棵维护 [ 1 , i ] 的数字出现情况。

则 [ L , R ] = [ 1 , R ] - [ 1 , L - 1 ] (前缀和思想)。

3.会不会空间超限

n棵线段树,每棵2n个点,怎么也是n^2的空间?

优化方法:注意到每棵新树都转移到上一棵树,只是改了一条从某叶子到根节点的路径 。

那么除了这条路径,其他的都可以直接从上一棵树上转移过来

大概就长这样了 ↓↓↓ 总共只用维护一棵树。空间复杂度O(nlogn)。

( 这个添加过程可以看做是:“链ys”加入“初始树xs”,即加入第一个元素 f 的过程 )

(d',g',f'都是新树相对于原树有修改的点,增加结点编号,把新旧结点都储存下来 )

 

当然,看到这里还是难以理解(反正本蒟蒻是没懂qaq

那就让本蒟蒻手动模拟一下试试吧...(像素渣&&字渣无误)

  • 以序列 4 1 3 2 为例,查询区间 [ 2,4 ] 中第3小的数。

用num[ ]记录原序列。首先“离散化+排序判重”,得到排序后的s数组 1 2 3 4 。

先确定初始线段树状态,再按照num[ ]的顺序,将 4 1 3 2 依次存入线段树中。

ONE POINT{很重要的理解}:下图圆圈内的数就是线段树存的权值,代表着:

统计(按照排名记录的区间)区间 [ i , j ] 中,已经存入的原序列的数(num[ ])的个数。

底层叶子节点记录的是,这个排名位置的数是否已经在树中建立。

0代表还没有建立,1表示已经建立,同时关联的上方管理数组的权值也会相应改变。

下面来模拟建树过程——

(状态1)原始空线段树,所有点的权值都为零。

(状态2)插入num[1]=4,到排序后它应该在的4号位置(是第四小的数)。

第一棵修改树可以用根节点序号标记为root[1]=8(具体root的实现看后面)。

状态3)插入num[2]=1,到排序后它应该在的1号位置(是第一小的数)。

状态4)插入num[3]=3,到排序后它应该在的3号位置(是第三小的数)。

状态5)插入num[4]=2,到排序后它应该在的2号位置(是第二小的数)。

这样,未优化空间的、插入元素使用的、5棵树就建好了。

为优化空间,可以使用树的合并(就是上面第三大点说的【优化空间复杂度】)。

 

主席树的两个主要函数:插入和合并。

插入:动态开点。很多时候线段树维护的区间很大,而能定义的空间是有限的,

所以我们就只给那些有用的点一个编号就可以了(只有在修改时被访问过的点才是有用的)。

虽然这样的线段树是残缺不全的,但也还是线段树。

合并:当两棵线段树的下标和维护的范围大小都一样,这两棵线段树就可以合并。

(null表示还没有存入的节点,圆圈中的数表示存储的下方的已插入结点的个数)

(图中最下方的数字指的是离散化后的排名情况...)

这里写图片描述

对于每个位置都建一颗线段树,其实是一条链。

然后再按照顺序把线段树都合并起来。第 i 棵线段树维护的就是 1~i  区间的信息。

通过合并可以知道,线段树满足可加性,那么得到的信息肯定可以相减。(↓前缀和思想)

对于每一次询问通过第 r 棵线段树和第 ( l - 1 ) 棵线段树作差,即可得到该区间的信息。

因为我们是以离散数组构建的主席树,那么从根节点出发,左子树部分的数必定不大于右子树部分的数。

于是就可以将左儿子的节点个数 x 与 k 做比较,若 k≤x,则第 k 小值一定在左子树里面,

若 x≤k,则第 k 小值一定在右子树里面,然后递归往下走,缩小范围。

注意,前者递归时, k 直接传下去即可,后者递归时,需要将 k 减去左子树的数的个数再传递这个 k 值

例如我们查找 [1,4] 中第 2 小的值,图示如下,绿色节点为该值存在的区间位置。

↑↑↑ 需要注意的是,第二个绿色节点才是绿色根节点的左子树,因为左子树表示的区间是靠前的那一半。 

方法步骤总结:

 

现在我们真正来解决区间询问 [ l , r ] 的问题。

解决方案就是将主席树 [1,r] 减去主席树 [1,l−1] 。

首先看到主席树的底层,全部是对数字个数的统计。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值