再修树状数组
近期发现不会树状数组了,恶补一下。
先看看基本的树状数组支持什么:
可以 \(O(\log n)\) 求前缀和。
可以 \(O(\log n)\) 修改某个数。
1.二进制拆分
实际上二进制拆分只是一种思想。
假如现在有一个数 \(x\) 。
满足 \(x=2^{i_k} + 2^{i_{k-1}}+...+2^{i_1}\)
且 \(i_k\geqslant i_{k-1} \geqslant ... \geqslant i_1\)
所以可以将区间 \(\left(0,x\right]\) 分为至多 \(\log x\) 个区间。
分别为 \(\left(x-2^{i_1},x\right]\) , \(\left(x-2^{i_1}-2^{i_2},x-2^{i_1}\right]\) , ... , \(\left(0,x-2^{i_1}-2^{i_2}-...-2^{i_{k-1}}\right]\)
所以求前缀和时可以只用 \(\log x\) 的时间。
2.修改以及查询的前提
现在令区间 \(\left(L,R\right]\) 的长度一定是 \(R\) 的二进制表示的最后一位 \(1\) 所对应的次幂。(实际上就是 \(lowbit(R)\))
然后定义 \(c_R=\sum\limits_{i=R-lowbit(R)+1}^{R} a_i\)
所以有下图
现在已经可以支持修改和查询了。
3.单点修改
从叶子节点上升到根节点,只需要不断的加 \(lowbit(x)\) 即可。
inline void add(int x,int k)
{
for(register int i=x;i<=MAXN;i+=lowbit(i))
c[i]+=k;
}
4.区间查询
不断的向下查询,遍历每一个下面的区间即可。
inline int ask(int x)
{
int ans=0;
for(register int i=x;i;i-=lowbit(i))
ans+=c[i];
return ans;
}

浙公网安备 33010602011771号