浅谈树状数组
前言
首先来通过一道题目来展开说明
已知一个数列,你需要进行下面两种操作:
- 将某区间每一个数数加上x
- 求出某一个数的值
数据规模:
对于30%的数据:N<=8,M<=10
对于70%的数据:N<=10000,M<=10000
对于100%的数据:N<=500000,M<=500000
蒟蒻: 暴力修改
dalao: 线段树模板题
这可以用线段树去做,但是线段树的代码量太长了,很容易打错.其实这一题可以用树状数组做.
概念
树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。
这种数据结构(算法)并没有C++和Java的库支持,需要自己手动实现。在Competitive Programming的竞赛中被广泛的使用。树状数组和线段树很像,但能用树状数组解决的问题,基本上都能用线段树解决,而线段树能解决的树状数组不一定能解决。相比较而言,树状数组效率要高很多。
来自百度百科
其实树状数组的本质就是进行区间操作,求区间的和,并支持修改操作
算法讲解
树状数组从字面上来解释就是树状的数组
这里的C数组就是树状数组,A数组是原数组
首先将C数组用A数组表示
C[1]=A[1];
C[2]=A[1]+A[2];
C[3]=A[3];
C[4]=A[1]+A[2]+A[3]+A[4];
C[5]=A[5];
C[6]=A[5]+A[6];
C[7]=A[7];
C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];
通过观察上面的式子我们可以发现一个规律:
C[i]=A[i-2k+1]+A[i-2k+2]+......A[i];(k为c数组下标的二进制中从最低位到高位连续零的长度)
但是这个东西我们怎么算呢?
这时候lowbit就很有用了
首先来看看lowbit的代码x&(-x)
别看代码只有一行实际上是很有用的,他蕴含着许多深意放屁
实际上lowbit就是计算2^k次方的值
然后你知道这个就应该知道如何用树状数组了
code
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
using namespace std;
int a[500005*3+10],n,m;
int lowbit(int x) {
return x&(-x);
}
inline void add(int x,int c) {
while(x<=n) {
a[x]+=c;
x+=lowbit(x);
}
}
inline int sum(int x) {
int ans=0;
while(x>0) {
ans+=a[x];
x-=lowbit(x);
}
return ans;
}
int main() {
int L,x,y;
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {
scanf("%d",&x);
add(i,x);
}
for(int ii=1; ii<=m; ii++) {
scanf("%d%d%d",&L,&x,&y);
if(L==1)
add(x,y);
else
printf("%d\n",sum(y)-sum(x-1));
}
return 0;
}