树状数组
树状数组的用处
快速求和+快速修改,可以理解为动态前缀和
建立
这就是树状数组能快速求解信息的原因:我们总能将一段前缀 $[1, n]$ 拆成 不多于 $\boldsymbol{\log n}$ 段区间,使得这 $\log n$ 段区间的信息是 已知的。
于是,我们只需合并这 $\log n$ 段区间的信息,就可以得到答案。相比于原来直接合并 n 个信息,效率有了很大的提高。
不难发现信息必须满足结合律,否则就不能像上面这样合并了。
树状数组的工作原理

对于树状数组每一个元素$c_i$
以下式子成立:
$C_i=\sum\limits_{x=i-lowbit(i)+1}^{i}$
$lowbit函数$
int lowbit(int x){
return x&(-x);
}
查询
从 $c[x]$ 开始往前跳,有 $c[x]$ 管辖 $a[x-\operatorname{lowbit}(x)+1 \ldots x]$; 令 $x \gets x - \operatorname{lowbit}(x)$,如果 $x = 0$ 说明已经跳到尽头了,终止循环;否则回到第一步。 将跳到的 $c_x$ 合并,即为$[1,x]$的区间和。
int getsum(int x){
int sum=0;
for(int i=x;i>0;i-=lowbit(i)) sum+=c[i];
return sum;
}
修改
管辖 $a[x]$ 的 $c[y]$ 一定包含 $c[x]$,所以 $y$ 在树状数组树形态上是 $x$ 的祖先。因此我们从 $x$ 开始不断跳父亲,直到跳得超过了原数组长度为止。
设 $n$ 表示 $a$ 的大小,不难写出单点修改 $a[x]$ 的过程:
初始令 $x' = x$。 修改 $c[x']$。 令 $x' \gets x' + \operatorname{lowbit}(x')$,如果 $x' > n$ 说明已经跳到尽头了,终止循环;否则回到第二步。
实现
void modify(int x,int d){
for(int i=x;i<=n;i+=lowbit(i)){
c[i]+=d;
}
}

浙公网安备 33010602011771号