树状数组是一种可以在O(log n)内区间求和或单点修改的数据结构,因其代码简单且不需递归,效率较线段树高。
树状数组的实现:
-
Lowbit:lowbit(k)是指整数k的二进制的右数第一个1以右的部分的大小,如(6)10=(110)2则lowbit(6)=(10)2=(2)10。
lowbit的简单实现:一个数加一个负号是把这个数的二进制取反+1,如-10的二进制就是-1010=0101+1=0110,则lowbit(10)=0110&1010=0010。即
1 #define lowbit(x) (x&(-x))
- 定义:数组a为储存原始数据的数组,数组c为树状数组。
-
实现树状数组:(并未完全理解,先记忆,回头复习)
初始化令c[k]=a[k]+a[k-1]+…+a[lowbit(k)];
1 for(i=1;i<=n;i++){ 2 cin>>a[i]; 3 a[i]+=a[i-1];//读入时用前缀和初始化 4 }; 5 for(i=1;i<=n;i++) 6 c[i]=a[i]-a[i-lowbit(i)];//初始化令c[k]=a[k]+a[k-1]+…+a[lowbit(k)];
则区间求和可以写为:
1 int get(int k){ 2 int sum=0; 3 while(k>0){ 4 sum+=c[k];//加上a[k]至a[lowbit(k)]的值 5 k-=lowbit(k);//将指针前移lowbit(k)个位置 6 } 7 return sum; 8 }
同样的,单点修改可以写为:
1 void add(int k,int x){ 2 while(k<=n){ 3 c[k]+=x;//将a[k]的值增加x,并更新c数组 4 k+=lowbit(k);//将指针前移lowbit(k)个位置 5 } 6 }
- 例题:codevs 1080(线段树练习)
#include<iostream> #include<cstring> #include<algorithm> #define lowbit(x) (x&(-x)) using namespace std; int a[200000],c[200000]; int i,j,n; int get(int k){ int sum=0; while(k>0){ sum+=c[k];//加上a[k]至a[lowbit(k)]的值 k-=lowbit(k);//将指针前移lowbit(k)个位置 } return sum; } void add(int k,int x){ while(k<=n){ c[k]+=x;//将a[k]的值增加x,并更新c数组 k+=lowbit(k);//将指针前移lowbit(k)个位置 } } int main(){ int k,m; memset(a,0,sizeof(a)); memset(c,0,sizeof(c)); cin>>n; for(i=1;i<=n;i++){ cin>>a[i]; a[i]+=a[i-1];//读入时用前缀和初始化 }; for(i=1;i<=n;i++) c[i]=a[i]-a[i-lowbit(i)];//初始化令c[k]=a[k]+a[k-1]+…+a[lowbit(k)]; cin>>m; for(i=1;i<=m;i++){ cin>>k; if(k==1){cin>>k>>j;add(k,j);} else{cin>>k>>j;cout<<get(j)-get(k-1)<<endl;} } }