树状数组总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组总结相关的知识,希望对你有一定的参考价值。
树状数组还是挺方便的,代码短功能也强大,完全可以用来替代一部分线段树的功能
有三种用法
一是对于单点更新,区间查询的
1 //hdu 1166 2 #include <stdio.h> 3 #include <string.h> 4 #include <iostream> 5 6 using namespace std; 7 8 int n,t; 9 int a[50008]; 10 int tree[50008]; 11 12 void add(int x,int num) 13 { 14 while(x<=n) 15 { 16 tree[x]+=num; 17 x+=x&-x; 18 } 19 } 20 21 int read(int x) 22 { 23 int sum = 0; 24 while(x) 25 { 26 sum+=tree[x]; 27 x-=x&-x; 28 } 29 return sum; 30 } 31 32 int main() 33 { 34 string tmp; 35 int b,c; 36 scanf("%d",&t); 37 for(int j = 1;j<=t;j++) 38 { 39 memset(tree,0,sizeof(tree)); 40 scanf("%d",&n); 41 printf("Case %d:\n",j); 42 for(int i = 1;i<=n;i++) 43 { 44 scanf("%d",&a[i]); 45 add(i,a[i]); 46 } 47 48 while(cin>>tmp,tmp!="End") 49 { 50 if(tmp=="Query") 51 { 52 scanf("%d%d",&b,&c); 53 printf("%d\n",read(c)-read(b-1)); 54 }else if(tmp=="Add") 55 { 56 scanf("%d%d",&b,&c); 57 add(b,c); 58 }else if(tmp=="Sub") 59 { 60 scanf("%d%d",&b,&c); 61 add(b,-c); 62 } 63 } 64 } 65 return 0; 66 }
二是对于单点更新,但是查询区间最大最小值的
1 //hdu 1754 2 #include <stdio.h> 3 #include <string.h> 4 #include <iostream> 5 6 using namespace std; 7 8 int n,m; 9 int a[200008]; 10 int tree[200008]; 11 12 int lowbit(int x) 13 { 14 return x&-x; 15 } 16 17 void update(int x) 18 { 19 while(x<=m) 20 { 21 tree[x]=a[x]; 22 for(int i=1;i<lowbit(x);i<<=1) 23 tree[x]=max(tree[x],tree[x-i]); 24 x+=lowbit(x); 25 } 26 return ; 27 } 28 int query(int x, int y) 29 { 30 int ans = 0; 31 while (y >= x) 32 { 33 ans = max(a[y], ans); 34 y --; 35 for (; y-lowbit(y) >= x; y -= lowbit(y)) 36 ans = max(tree[y], ans); 37 } 38 return ans; 39 } 40 41 int main() 42 { 43 char tmp; 44 int c,d; 45 while(scanf("%d%d",&m,&n)!=EOF) 46 { 47 memset(tree,0,sizeof(tree)); 48 for(int i = 1;i<=m;i++) 49 { 50 scanf("%d",&a[i]); 51 update(i); 52 } 53 getchar(); 54 for(int i = 1 ;i <= n;i++) 55 { 56 scanf("%c%d%d",&tmp,&c,&d); 57 if(tmp==‘U‘) 58 { 59 a[c] = d; 60 update(c); 61 } 62 else if(tmp==‘Q‘) 63 { 64 printf("%d\n",query(c,d)); 65 } 66 getchar(); 67 } 68 } 69 return 0; 70 }
三是对于区间更新,然后区间查询
这个区间更新主要是要用到一个差分数组
我们假设sigma(r,i)表示r数组的前i项和,调用一次的复杂度是log2(i)
设原数组是a[n],差分数组c[n],c[i]=a[i]-a[i-1],那么明显地a[i]=sigma(c,i),如果想要修改a[i]到a[j](比如+v),只需令c[i]+=v,c[j+1]-=v
这样c[i]->C[j]便会相当与加了j-i个v
怎么实现“区间修改,区间查询”呢?
观察式子:
a[1]+a[2]+...+a[n]
= (c[1]) + (c[1]+c[2]) + ... + (c[1]+c[2]+...+c[n])
= n*c[1] + (n-1)*c[2] +... +c[n]
= n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n])
那么我们就维护一个数组c2[n],其中c2[i] = (i-1)*c[i]
每当修改c的时候,就同步修改一下c2,这样复杂度就不会改变
那么
式子①
=n*sigma(c,n) - sigma(c2,n)
于是我们做到了在O(logN)的时间内完成一次区间和查询
1 //poj 3468 2 #include <stdio.h> 3 #include <string.h> 4 #include <iostream> 5 #define Nmax 200008 6 7 using namespace std; 8 9 int n,m; 10 long long a[200008]; 11 long long tree[200008]; 12 long long sum[200008]; //原始的前缀合 13 long long c1[Nmax]; //前缀和 14 long long c2[Nmax]; //前缀和*i-1 15 16 int lowbit(long long x) 17 { 18 return x&-x; 19 } 20 21 void add(long long *array,long long x,long long num) 22 { 23 while(x<=n) 24 { 25 array[x] += num; 26 x+=x&-x; 27 } 28 } 29 30 31 long long read(long long *array,long long x) 32 { 33 long long sum = 0; 34 while(x) 35 { 36 sum += array[x]; 37 x-=x&-x; 38 } 39 return sum; 40 } 41 42 int main() 43 { 44 scanf("%d%d",&n,&m); 45 char tmp; 46 long long l,r,q; 47 for(int i = 1;i<=n;i++) 48 { 49 scanf("%lld",&a[i]); 50 add(c1,i,a[i]-a[i-1]); 51 add(c2,i,(i-1)*(a[i]-a[i-1])); 52 } 53 getchar(); 54 for(int i = 1;i<=m;i++) 55 { 56 scanf("%c",&tmp); 57 if(tmp==‘C‘) 58 { 59 scanf("%lld%lld%lld",&l,&r,&q); 60 add(c1,l,q); 61 add(c1,r+1,-q); 62 add(c2,l,(l-1)*q); 63 add(c2,r+1,-q*r); 64 }else if(tmp==‘Q‘) 65 { 66 scanf("%lld%lld",&l,&r); 67 long long ans1 = (l-1)*read(c1,l-1)-read(c2,l-1); 68 long long ans2 = (r)*read(c1,r)-read(c2,r); 69 printf("%lld\n",ans2-ans1); 70 } 71 getchar(); 72 } 73 return 0; 74 }
以上是关于树状数组总结的主要内容,如果未能解决你的问题,请参考以下文章