树状数组入门及例题题解(一)——区间求和及单点修改

  • Post author:
  • Post category:其他




树状数组是什么

树状数组是一种非常适合于求前缀和的工具(或者是其他的一些有关前缀的东西,例如区间最大值等等) 尤其是涉及到单点修改的操作的时候 如果是用普通的线性数组 和 线性的求和数组的话 复杂度会达到O(n)

而使用树状数组 则可以将这个复杂度 降低至O(logn)



怎么使用树状数组

假设用一个a数组来储存这个区间中的元素(索引从1开始)

树状数组的最基本的使用 主要有四个部分组成

  1. int c[maxn] 一个数组 用来储存前缀和
  2. int lowbit(int x) 一个函数 用来求下标
  3. void update(int position,int value) 一个函数 用来进行单点修改
  4. int get_sum(int x) 一个函数 用来求从区间左端点 到 x 的元素的和

树状数组示意图



树状数组储存的原理

——利用lowbit()函数来计算下标



lowbit是什么

:一个二进制数从右到左的第一个1

就比如说 十进制数12 转化为二进制数就是 1100 那么它的从右到左的第一个1就是 100 转化为十进制数就是4

这个操作可以用lowbit()函数非常快的求出来

代码如下:

int lowbit(int x){
	return x&(-x);
	}

这个的原理不讲 举个例子看一看:

就比如说之前的那个12的例子

-12 的补码形式是 1000……0000100

12 的补码形式是 0000……0001100

那么在进行位与操作 求得答案 100 转化为十进制也就是 4



储存

知道了lowbit之后 就可以用这个来创建一个树状数组了

前面说了 在这里用一个c数组来储存

规定 c[i] 这个元素 储存的是 区间内 第i-lowbit(i)+1 个元素 到第 i个元素的和

那么对于区间中的某一个元素a[i]而言 它对那些c数组中的元素产生了影响呢?

1、首先c[i]这个元素肯定被影响到了 因为这个元素储存的就直接是 区间内 第i-lowbit(i)+1 个元素 到第 i个元素的和

而且所有索引小于i的元素都没有被影响到

2、然后 第i+lowbit(i)个元素也被影响到了 c[i+lowbit(i)]

因为可以计算出来 lowbit(i+lowbit(i)) 必然大于等于 lowbit(i) 这个非常浅显因为i加上一个lowbit(i)之后 最后一个1的位置必然往前进一位 故再求得的 lowbit(i+lowbit(i)) 会更大

例如一个数i =……1000

它的lowbit(i)=1000

故i+lowbit(i)=……10000

再求lowbit(i+lowbit(i))=10000 > 1000

3、那么在第i个元素 到第 i+lowbit(i) 个元素之间是否有元素被影响到呢?

答案是没有。

我们可以假设有一个数i = ……1000

那么这个数i 求lowbit(i)=1000

那么i+lowbit(i) =……10000

那么在i到i+lowbit(i)之间的所有的数就是

……1001 ~ ……1111

这些数(设为x) 所储存的和的左端点为 x-lowbit(x)+1 > i

所以 都必定取不到i这个数

4、再根据数学归纳法的递推原理

while(){
	//在其中添加逻辑
	i=i+lowbit(i);
}

这个循环里面的所有元素均会被影响到,但是这样必然进入一个死循环 无法跳出

但是其实我们并不需要处理这么多元素 只需要处理到这个区间的右端点就可以了

于是这个循环可以写成这样:

while(i<=right_restrain){
	//在其中添加逻辑
	i+=lowbit(i);
}



单点更新——update()

知道了树状数组的储存原理 以及每一个元素储存了区间中那哪一些值之后 进行单点修改操作就很简单了

已知 将要进行修改的元素的位置 并且知道它的变化量

void update(int position,int value){
	//表示要将区间position的元素的值增加value(value可以是负数)
	while(position<=right_restrain){
		c[position]+=value;
		position+=lowbit(position);
	}
}



区间求和

因为c数组中的元素c[i]储存 的是区间内 第i-lowbit(i)+1 个元素 到第 i个元素的和

故如果我们要求从这个区间的第一个元素加到第i个元素的和

就可以这样做

c[i]+c[i-lowbit(i)]+……

最后直到i-lowbit(i)等于0为止(因为对于上一个元素已经加到了 i-lowbit(i)+1 而i-lowbit(i)又等于0 所以就表示已经加到了区间的第一个元素了)

代码如下:

int get_sum(int position){
	//表示要求区间中 第一个元素到第position个元素的和
	int sum=0;
	while(position){
		sum+=c[position];
		position-=lowbit(position);
	}
	return sum;
}

至于要求第i个元素到第j个元素的和(i<j)就很简单了

int getSum(int i,int j){
	return get_sum(j)-get_sum(i-1);
}

这样最简单的树状数组的单点修改和区间求和操作就完成了



树状数组经典入门例题讲解


树状数组入门及例题题解(二)——排兵布阵



树状数组进阶



区间最值


树状数组入门及例题讲解(三)——区间最值



二维树状数组



版权声明:本文为i_want_to_studyi原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。