再修树状数组

近期发现不会树状数组了,恶补一下。

先看看基本的树状数组支持什么:

可以 \(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\)

所以有下图

本图来自 OI-wiki

现在已经可以支持修改和查询了。

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;
}
posted @ 2022-03-09 22:17  yzh_Error404  阅读(28)  评论(0)    收藏  举报