前言
好久之前就讲过,但还是用得不是很熟练(难道是因为我总是写暴力?)。还是复习一下吧,由于是复习,可能会不是很详细,比起原理更侧重于用法什么的。
如果之前没有接触过,还是看这篇博文吧。(是我入门的时候看的博客)
原理
简单说一下吧。
其实树状数组和二进制有着密切的关系。
就像所有的整数都可以表示成2的次方的和那样,我们也可以考虑把一串序列表示成一系列子序列的和,将一个前缀和划分成了多个子序列的和,而划分的方法与数的2的幂和具有及其相似的方式。
左边是空的树状数组的形态,右边是放了值进去的树状数组。
假设我们要计算$a[1]..a[13]$的和,即$sum[13]$ 由于$13$的二进制表示为$1101$,那么就有:
$sum[1101] = tree[1101] + tree[1100] + tree[1000]$
可以用最简单的位运算实现取出一个整数的二进制形式中的最右一个“1”的位置: i&(-i)
这个数有个专门的称呼,叫$lowbit$
求前缀和的图示
应用-单点修改,区间查询
最基础的类型,就不多说了。
1 #include<cstdio> 2 using namespace std; 3 #define N 500005 4 int n,m; 5 int a[N],tree[N]; 6 int rd() 7 { 8 int f=1,x=0;char c=getchar(); 9 while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();} 10 while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();} 11 return f*x; 12 } 13 int lowbit(int x) 14 { 15 return x&(-x); 16 } 17 int query(int x) 18 { 19 int ans=0; 20 for(;x;x-=lowbit(x)) 21 ans+=tree[x]; 22 return ans; 23 } 24 void update(int x,int val) 25 { 26 for(;x<=N;x+=lowbit(x)) 27 tree[x]+=val; 28 return ; 29 } 30 int main() 31 { 32 n=rd(),m=rd(); 33 for(int i=1;i<=n;i++) 34 { 35 a[i]=rd(); 36 update(i,a[i]); 37 } 38 for(int i=1;i<=m;i++) 39 { 40 int opt=rd(),x=rd(),y=rd(); 41 if(opt==1) update(x,y); 42 if(opt==2) printf("%d\n",query(y)-query(x-1)); 43 } 44 return 0; 45 }