树状数组的原理和基础应用
这样的数据结构称作树状数组,它支持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
题意:单点修改,区间查询
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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\n",sum(y)-sum(x-1)); 30 } 31 } 32 }
例题2 :树状数组2 https://www.luogu.com.cn/problem/P3368
题意:区间修改,区间查询
题解:常用技巧:用树状数组维护差分数组,查询时快速前缀和
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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\n",sum(x)); 26 } 27 } 28 }
例题3 [SDOI2009]HH的项链https://www.luogu.com.cn/problem/P1972
题意:给出一串项链中每一个贝壳属于哪一种,给出m组询问,求给定区间内贝壳的种类数。
题解:离线思想,把询问按右端点排序并记录原来的顺序。
记录上一次查到了last-1,那么这一次只需更新last到q[i].r,如果这某一种之前出现过就在之前的节点减一。把现在的节点加一。
然后把前缀和一减就是区间和。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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\n",ans[i]); 47 }