树状数组上二分
这篇博客写得挺好的:blog
利用了树状数组 \(tr\) 的核心特性:\(tr[x]\) 维护区间 \((x - lowbit(x), x]\) 上的信息。
可以在 \(O(\log n)\) 内解决 动态全局第 \(k\) 小问题。相比用平衡树维护,代码量简短了太多。
Q:维护对一个可重集的三种操作:删一个数字,添一个数字,查询第 \(k\) 小。
按照值域建立并维护树状数组。在面对查询时,定义起始位置 \(p=0\),考虑二分整个值域(类似线段树上二分,每次进入左子区间或右子区间): 降序枚举倍增级数 \(w\),将 \((p, p + 2^{w}]\) 视为左子区间,\((p + 2^{w}, p + 2^{w+1}]\) 视为右子区间,此时我们只需要知道在左子区间值域上包含的元素数量,就可以决策下一步进入左子区间还是右子区间。而这个数量恰好可以利用树状数组 \(tr\) 的特性 \(O(1)\) 得到 —— 就是 \(tr[p+2^{w}]\)(具体证明见上面的 blog),于是我们只需要判断 \(tr[p+2^{w}]\) 与 \(k\) 的关系:
- \(tr[p+2^{w}]<k\) \(\rightarrow\) 走右子区间 \(\rightarrow\) \(k = k - tr[p+2^{w}], p = p + 2^{w}, w = w/2\);
- \(tr[p+2^{w}]\geq k\) \(\rightarrow\) 走左子区间 \(\rightarrow\) \(w = w/2\)。
最终 \(p+1\) 就是我们要求解的答案。注意需要判断 \(p+2^{w}\) 是否超出值域最大范围。

浙公网安备 33010602011771号