树状数组倍增(二分)
Unaccepted_Zhou
·
·
算法·理论
维护多重集合:
---
首先显然可以权值线段树二分做。
实际上树状数组也可以做,可能有部分人不知道。
:::info[思考过程]
类似于线段树,考虑从最大的块 $2^w$ 开始。
如果这一块 $<k$ 找右子树,否则找左子树。
找右子树相当于将当前位置增大 $2^w$,向左不变。
然后以此类推。
:::
其实就是从 $0$ 开始的倍增。
:::info[正确性]
考虑当前位置 $p$,倍增 $2^w$,需要判断 $(p,p+2^w]$。
显然 $2^{w+1}\mid p$。
则有 $2^{w+1}\nmid p+2^w$,$2^w\mid p+2^w$。
则有 $p+2^w$ 的 lowbit 为 $2^w$。
则有树状数组上 $c_{p+2^w}=(p,p+2^w]$。
:::
:::success[code]
```cpp
ll p=0,k=/*输入*/;
for(ll w=1<<21;w;w>>=1)
if(p+w<=n&&c[p+w]<k) p+=w,k-=c[p];
printf("%lld\n",p+1);
//注意:一定加 <=n,否则溢出部分的 c 全是 0
```
:::
时间复杂度:显然小常数 $O(n\log{n})$。
用于:
- 单点修改。
- **全局**二分。
区间修改或区间内二分请使用线段树。