先说下对树状数组主要部分的理解:
lowbit x&(-x) 依照补码的特性,取反后第一位(低到高)原本是1的数 会变成一其他取反,也就导致了返回最高的低位连续0位置
区间查询 以区间和的形式来理解
int query(int x){ int ans=0; while(x>0){ ans+=sum[x]; x-=lowbit(x); } return ans; }x-lowbit(x)意味着x的二进制位从低到高把1变为0 直到全部为0;因为sum[i]=a[i-2^k+1]……a[i] 也就是sum[i]可以表示出从i----i-lowbit(i) +1这个范围的和 一次查找就得到了i--0的区间和时间复杂度显然是O(log(n))
区间更新:
void add(int i,int va){ while(i<=n){ sum[i]+=va; i+=lowbit(i); } }
在这们的目的是 把包含a[i]的sum[x] 都更新一边 ,也就是我们要通过i去找x,从上面我们可以知道x包含的范围是前连续0最高位 由1变0+最低位+1 的范围
所以找出最低位的1看做是小的范围一次向上去查找就是包含它的sum[i](相当于遍历了所有处于这两个数范围的值)
上一道模板题 (跟线段树的模板题一样,可以对比学习):http://acm.hdu.edu.cn/showproblem.php?pid=1166
#include<bits/stdc++.h> using namespace std; long long sum[50004]; int n; int lowbit(int x){ return x&(-x); } int query(int x){ int ans=0; while(x>0){ ans+=sum[x]; x-=lowbit(x); } return ans; } void add(int i,int va){ while(i<=n){ sum[i]+=va; i+=lowbit(i); } } int main(){ // freopen("1.txt","r",stdin); int t; cin>>t; for(int j=1;j<=t;j++){ cout<<"Case "<<j<<":"<<endl; cin>>n; memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++){ int x; cin>>x; add(i,x); } char s[10]; while(cin>>s&&s[0]!='E'){ int x,y; cin>>x>>y; if(s[0]=='Q') cout<<query(y)-query(x-1)<<endl; if(s[0]=='A') add(x,y); if(s[0]=='S') add(x,-y); } } }