树状数组入门(求和)

Posted lcxer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组入门(求和)相关的知识,希望对你有一定的参考价值。

Description

输入一个数列A1,A2….An(1<=N<=100000),在数列上进行M(1<=M<=100000)次操作,操作有以下两种:
(1)格式为C I X,其中C为字符"C",I和X(1<=I<=N,|X|<=10000)都是整数,表示把把a[I]改为X
(2)格式为Q L R,其中Q为字符"Q",L和R表示询问区间为[L,R](1<=L<=R<=N),表示询问A[L]+…+A[R]的值。

Input

第一行输入N(1<=N<=100000),表述数列的长度,接下来N行,每行一个整数(绝对值不超过10000)依次输入每个数
;接下来输入一个整数M(1<=M<=100000),表示操作数量,接下来M行,每行为C I X或者Q L R。

Output

对于每个Q L R 的操作输出答案。

Sample Input

5
1
2
3
4
5
3
Q 2 3
C 3 9
Q 1 4

Sample Output

5
16

 

图大家自己上网找,这里解释树状数组的构造

假如有两个数组,其中一个是a数组,另一个是树状数组t数组

树状数组类似前缀和,但是效率要高得多

首先,我给大家列举一下:c[i]初始等于a[i]

t[1]=a[1],t[2]=t[1]+a[2],t[3]=a[3],t[4]=t[2]+t[3],t[5]=a[5],t[6]=t[2]+a[6],t[7]=a[7],t[8]=t[7]+t[6]+t[4];

然后大家可以发现一个规律,树状数组t后缀为奇数就是数组a中的本身,转成二进制后就很直观了

就拿8举个例子,8转成二进制后是1000

k代表的是转成二进制之后的出现0的位置,二进制最右一位是2^0,那么我们可以发现(n=8)n-2^k恰好就是t数组加上的

就像8二进制第一个出现0的位置是第0个,那么n-2^0=8-1=7,t[8]确实加了t[7]。

这是不是偶然呢?显然是不可能的,不信我们继续看,8二进制第二个0出现在第1个位置,那么n-2^1=8-2=6,t[8]也确实加了t[6],这样一来n-2^2=8-4=4,0走完了,t[8]也加完了,这个是真实存在的现象,然后也会很好记忆,如果还有疑惑可以自己去推其他的

下面给出此题代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int maxn=100010;
 4 int n,m;int a[100010];
 5 int c[100010];
 6 char s[2];
 7 int lowbit(int p)
 8 {
 9     return (p&(-p));
10 }
11 void add(int p,int num)
12 {
13     while (p<=n)
14     {
15         c[p]+=num;
16         p+=lowbit(p);
17     }
18     return;
19 }
20 int query(int p)
21 {
22     int tmp=0;
23     while (p)   
24     {   
25         tmp+=c[p];      
26         p-=lowbit(p);   
27     }   
28     return tmp;
29 }
30 int main()
31 {   
32     scanf("%d",&n);             
33     for (int i=1;i<=n;i++)       
34     {           
35         scanf("%d",&a[i]);          
36         add(i,a[i]);        
37     }       
38     scanf("%d",&m);     
39     for(int i=1;i<=m;i++)        
40     {       
41         scanf("%s",s);          
42         if (s[0]==C)          
43         {           
44             int j,k;                
45             scanf("%d%d",&j,&k);                
46             add(j,k-a[j]);              
47             a[j]=k; 
48         }
49         else           
50         {           
51             int l,r;            
52             scanf("%d%d",&l,&r);                
53             int sum=query(r)-query(l-1);                
54             printf("%d
",sum);         
55         }       
56     }   
57     return 0;
58 }

 




以上是关于树状数组入门(求和)的主要内容,如果未能解决你的问题,请参考以下文章

树状数组区间修改和区间求和

树状数组2 - 区间加 单点求和

区间修改区间求和的树状数组

树状数组区间加区间求和

hdu1166 敌兵布阵 树状数组/线段树

资瓷区间修改+区间求和的树状数组(一维/二维)