树状数组

引入

如果,让你求一段数的和,你会怎么求?

你当然可以用前缀和,在 \({O(n)}\) 的预处理后查询只需要 \({O(1)}\) 的复杂度。

但是,如果我还要修改呢?

比如说最不理想的情况下,我每次都修改第一个数,前缀和数组每次就要花 \({O(n)}\) 的时间复杂度来维护。
我如果修改m次,你就要花 \({O(nm)}\) 复杂度,太大了。

那么,有个东西叫做树状数组,它使用内存相当于线性数组,同时又能建立一个类似树的结构,在求前缀和时的复杂度和修改的复杂度都是 \({log\ n}\) 级别的,复杂度好多了。

结构

定义一个线性数组,

每一位数组管辖着从自身开始往前的某一段范围,有些类似于线段树。

但怎么规定好这个范围?这就涉及树状数组的核心操作——\({lowbit}\).

树状数组结构

lowbit

为该数二进制下最后一个1和其后的0所表示的十进制数。

\({x \& (-x) }\)

你要找前驱时就是 \({i-lowbit(i)}\)

父节点是 \({i+lowbit(i)}\)

这利用了二进制的特性,在此不作过多解释。

因此,修改时一般是从下往上,而访问时则相反,是从上往下。


附:

修改code:

void update(int pos,int k) {
   for(int i=pos;i<=n;i+=lowbit(pos))
        b[pos]+=k;
   return ;
}

查询code:

void query(int pos) {
   for(int i=pos;i>=1;i-=lowbit(pos))
   	sum+=b[pos];
   return ;
}
posted @ 2023-04-29 07:03  ForBiggerWorld  阅读(17)  评论(0)    收藏  举报