P2617 Dynamic Rankings
前言
这是一道已经咕了很久的题。
第一次想写是因为 luan 讲了这道题。
直到她又出现在了我的智能推荐里。
我知道我不能再等了!
我要去和她表白!!
类题总结
关于各种第 k 大问题肯定已经见过很多了。
反正各种奇奇怪怪的解法都是有的。
比较主流的有:
静态整体第 k 大(小)
这玩意有两种解法。
首先是显而易见的 std::sort
.
|
生怕你不会写这玩意
时间复杂度 $\text{O(nlogn)}$。
之后这玩意是存在 $\text{O(n)}$ 做法的。
考虑魔改快排。
每次选择基准值的时候记录一些大于基准值的个数 $cnt$ ,之后根据 $cnt$ 选择一半进入,就可以在平均线性之间内统计出第 k 大(小)。
|
动态整体第 k 大(小)
权值线段树,插入直接在树上插入,然后查询也直接查就行,像个桶一样。
值域小一点的话直接建树就行了,大一点就动态开点。
|
复杂度 $\text{O(nlogn)}$
静态区间第 k 大(小)
也就是主席树板子题了。
具体看这个就行:p3834 可持久化线段树模板(主席树)
动态区间第 k 大(小)
就是本题了。
考虑修改操作。
由于常规的主席树是依靠前缀和的思想来求解区间第 k 大的,于是可以想到求解动态区间第 k 大首要问题是如何维护前缀和的问题。
对于维护前缀和,我们通常有如下几种方式:
暴力,树状数组,线段树。
于是有一个思路:加一个数据结构来维护前缀和。
加上去的数据结构放在什么位置呢?
由于主席树需要前缀和来作差,于是可以发现树状数组是要套在主席树外面的。
于是明白树状数组每个节点都维护一个线段树的根节点。
分开考虑修改以及查询操作。
修改操作
树状数组每次单点修改复杂度 $\text{O(logn)}$ ,对应 $\text{O(logn)}$ 棵权值线段树会受到影响,又因为每次修改需要对应到线段树内,线段树内每次又会有 $\text{O(logn)}$ 个节点需要修改。于是单次复杂度 $\text{O}(log^2n)$
每次修改需要用树状数组定位到需要修改的线段树的根节点位置,之后进行修改即可:
|
查询操作
对于查询操作,依然是要用到前缀和。
由于使用了树状数组维护前缀和,于是我们直接在树状数组上取出对应的 $\text{O(logn)}$ 个根节点,也就是对应的主席树根节点,取的时候直接体现作差就行。
之后就和普通的主席树查询区间第 k 大一样,根据作差得到的值直接递归进入左子树或者右子树即可。
复杂度 $\text{O}(log^2n)$
|
复杂度分析
查询操作 $\text{O}(log^2n)$,修改操作 $\text{O}(log^2n)$,总体复杂度 $\text{O}(mlog^2n)$
由于动态开点,所以空间复杂度就是对应的查询和修改所需要的空间。
于是 空间复杂度和时间复杂度可以认为是同阶的。
线段树大小开 $400$ 倍就够了。
具体实现的时候建议封一个 namespace
,看着也好看调着也爽。
似乎代码没有想象的那么长?
CODE:
|