初始给一个数组,然后对这个数组中的元素进行加减操作,比如add i x,就是把数组中第i个元素加x;sub减法就可以看做是加了一个负数;然后要完成一个查询操作,比如query a b(b>a),就是要查询数组中第a个元素到第b个元素的和(区间和)
普通的做法就是建立一个数组,直接加减求和,加减都可以直接O(1)完成,但是区间求和的过程比较麻烦,需要O(n)的复杂度;
所以我们建立一个树状数组,来以较低的复杂度完成区间求和这个操作。
首先引入lowbit这个函数,定义 lowbit(i)=i&(-i)
然后我们构建树状数组c,假设我们原有的数组是a数组(其实不存在),那么,c[i]表示从a[i-lowbit(i)]到a[i]这lowbit(i)个数的和。
为了方便描述,我copy了一点描述过来:
如上图所示
c1 = a1
c2 = a1 + a2
c3 = a3
c4 = a1 + a2 + a3 + a4
c5 = a5
c6 = a5 + a6
c7 = a7
c8 = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8
对于序列a,我们设一个数组C定义C[i] = a[i – 2^k + 1] + … + a[i] , k=lowbit(i)(这是为啥我也不知道)
具体的我也没太理解,但是可以简单的用两个add和sum函数来描述树状数组的一些操作,如下:
int lowbit(int k) {return k&(-k);}
void add(int x, int y){
while(x<=n){
c[x]+=y;
x+=lowbit(x);
}
return ;
}
int sum(int x){
int sum=0;
while(x > 0){
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
相信我虽然现在不太理解,多写几次应该就理解了吧!:D
顺便贴上完整ac代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int maxx=50010;
int c[maxx];
int n;
int lowbit(int k) {return k&(-k);}
void add(int x, int y){
while(x<=n){
c[x]+=y;
x+=lowbit(x);
}
return ;
}
int sum(int x){
int sum=0;
while(x > 0){
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
int main (){
int T,x,y,xu=1;
string s;
cin>>T;
while(T--){
memset(c, 0, sizeof(int)*maxx);
printf("Case %d:\n",xu++);
cin>>n;
for(int i=1;i<=n;i++){
cin>>x;
add(i,x);
}
while(1){
cin>>s;
if(s[0]=='E') break;
else if(s[0]=='A'){
cin>>x>>y;
add(x,y);
}
else if(s[0]=='S'){
cin>>x>>y;
add(x,-y);
}
else if(s[0]=='Q'){
cin>>x>>y;
cout<<sum(y)-sum(x-1)<<endl;
}
}
}
return 0;
}
错点:
1.memset操作要在while(T--)里面进行,因为每一次的数组都是不一样的初始值,每次操作前都要初始化c数组