树状数组的原理和基础应用

Posted vv123

tags:

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

技术图片

这样的数据结构称作树状数组,它支持O(logN)的单点修改和区间查询,效率高并且代码简洁,缺点在于适用范围不如线段树广。
不难看出(雾),tree[i]表示a[i]及之前的 lowbit(i)个 数,定义lowbit(i)等于取i的二进制中最后一个‘1‘表示的大小
观察发现(。),修改a[i]只需更新包含i的节点,倒推可知从i开始,每次把i加上lowbit(i),这些节点包含了a[i]
从i开始,每次把i减去lowbit(i)直到为0,这些节点的值加起来就是前缀和。
例题1:树状数组1  https://www.luogu.com.cn/problem/P3374
题意:单点修改,区间查询

技术图片
 1 #include<bits/stdc++.h> 
 2 using namespace std;
 3 int n,m,opt,k,x,y,a,tree[500005];
 4 int lowbit(int i){
 5     return i&-i;
 6 }
 7 void add(int i,int a){
 8     for(;i<=n;i+=lowbit(i))
 9     tree[i]+=a;
10 }
11 int sum(int i){
12     int s=0;
13     for(;i;i-=lowbit(i))
14       s+=tree[i];
15   return s;
16 }
17 int main(){
18     //freopen("out.txt","w",stdout);
19   scanf("%d%d",&n,&m);
20     for(int i=1;i<=n;i++)    scanf("%d",&a),add(i,a);
21     while(m--){
22         scanf("%d",&opt);
23         if(opt==1){
24             scanf("%d%d",&x,&k);
25             add(x,k);
26         }
27         if(opt==2){
28             scanf("%d%d",&x,&y);
29             printf("%d
",sum(y)-sum(x-1));
30         }
31     }
32 }
View Code

 


例题2 :树状数组2 https://www.luogu.com.cn/problem/P3368
题意:区间修改,区间查询
题解:常用技巧:用树状数组维护差分数组,查询时快速前缀和

技术图片
 1 #include<bits/stdc++.h> 
 2 int n,m,a,opt,x,y,k,d[500005],tree[500005]; 
 3 int lowbit(int i){
 4     return i&-i;
 5 }
 6 void add(int i,int a){
 7     for(;i<=n;i+=lowbit(i))  tree[i]+=a;
 8 }
 9 int sum(int i){
10     int s=0;
11     for(;i;i-=lowbit(i))s+=tree[i];
12     return s;
13 }
14 int main(){
15     scanf("%d%d",&n,&m);
16     for(int i=1;i<=n;i++)scanf("%d",&d[i]),add(i,d[i]-d[i-1]);
17     while(m--){
18         scanf("%d",&opt);
19         if(opt==1){
20             scanf("%d%d%d",&x,&y,&k);
21             add(x,k);add(y+1,-k);
22         }
23         if(opt==2){
24             scanf("%d",&x);
25             printf("%d
",sum(x));
26         }
27     }
28 }
View Code

 

 

例题3 [SDOI2009]HH的项链https://www.luogu.com.cn/problem/P1972
题意:给出一串项链中每一个贝壳属于哪一种,给出m组询问,求给定区间内贝壳的种类数。
题解:离线思想,把询问按右端点排序并记录原来的顺序。
记录上一次查到了last-1,那么这一次只需更新last到q[i].r,如果这某一种之前出现过就在之前的节点减一。把现在的节点加一。
然后把前缀和一减就是区间和。

技术图片
 1 #include<bits/stdc++.h>
 2 #define lowbit(i) (i&-i)
 3 using namespace std;
 4 int n,m,a[1000006],tree[1000006],book[1000006],ans[1000006],last;
 5 struct Query{
 6   int l,r,pos;
 7 }q[1000006];
 8 
 9 bool cmp(Query a,Query b){
10   return a.r<b.r;
11 }
12 
13 void add(int i,int a){
14   for(;i<=n;i+=lowbit(i))
15     tree[i]+=a;
16 }
17 
18 int sum(int i){
19   int ret=0;
20   for(;i;i-=lowbit(i))
21     ret+=tree[i];
22   return ret;
23 }
24 
25 int main(){
26   scanf("%d",&n);
27   for(int i=1;i<=n;++i)
28     scanf("%d",&a[i]);
29   scanf("%d",&m);
30   for(int i=1;i<=m;++i)
31     scanf("%d%d",&q[i].l,&q[i].r),q[i].pos=i;
32   sort(q+1,q+1+m,cmp);
33   
34   int last=1;
35   for(int i=1;i<=m;++i){
36       for(int j=last;j<=q[i].r;++j){
37         if(book[a[j]])
38           add(book[a[j]],-1);
39         add(j,1);
40         book[a[j]]=j;
41     }
42     last=q[i].r+1;
43     ans[q[i].pos]=sum(q[i].r)-sum(q[i].l-1);
44   }
45   for(int i=1;i<=m;++i)
46     printf("%d
",ans[i]);
47 } 
View Code

 

以上是关于树状数组的原理和基础应用的主要内容,如果未能解决你的问题,请参考以下文章

树状数组的基础知识

POJ-2155-Matrix二位树状数组应用

每日基础算法线段树 - 树状数组

POJ 2155 Matrix(树状数组+容斥原理)

树状数组的原理和实现

重见树状数组