树状数组简介(洛谷P3368、P3374)
2017-08-04 16:45
405 查看
算法用途
树状数组是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和。虽然树状数组的用途可以完全被线段树所替代,而且线段树所能做的比树状数组多得多,但是树状数组的常数是远远小于线段树的。因此当你写线段树被卡常时可以试试使用树状数组。
而且树状数组的代码很短哦!
算法思想
开一个数组s,其中s[i]的值存的是a[i-lowbit(i)]~a[i]这段区间的和。然后进行一系列操作。差不多是这样一个图(这里是c数组):
![](https://gss1.bdstatic.com/9vo3dSag_xI4khGkpoWK1HF6hhy/baike/w=268/sign=31480605d62a283443a6310d63b5c92e/caef76094b36acaf7fcc80e47ed98d1001e99cfd.jpg)
像不像一棵树啊#(滑稽)
关于lowbit
lowbit是什么啊!看上去好高级的样子!low:低,bit:比特(二进位制信息单位)(其实就是1啦) lowbit就是一个数在二进制中最低的1的位置。
举个栗子:5在2进制下为101,lowbit(5)=1。8在二进制下为1000,lowbit(8)=4。
那要怎么算lowbit(x)呢?
给段代码自己理解下
int lowbit(int x){ return x&(-x); }
对,你没看错,就是这么简单!可能会补码和反码的同学已经看懂了,这里给一脸蒙圈的解释一下。
设x的二进制码为 1000101011,那么-x-1的二进制码就是把x的二进制码全反过来:0111010100,-x的二进制码就是:0111010101,x&(-x)=1,就是x的lowbit了。(不信的小伙伴可以自己试试)
单点修改区间求和
单点修改
当a[x]改变时,s[x]会改变,s[x+lowbit(x)]会改变,s[x+lowbit(x)+lowbit(x+lowbit(x))]也会改变······一直到x超过n才停止。于是我们就可以尝试写出代码:
void nsrt(int x,int w){ while (x<=n){ s[x]+=w; x+=lowbit(x); } }
区间求和
树状数组的区间求和运用到了前缀和的思想,求l~r的和即求1~r的和减去1~(l-1)的和。那么1~x的和怎么求呢?
前面说过,s[x]存的是a[x-lowbit(x)]~a[x]的和,那么a[x-lowbit(x)]~a[x]的和我们是已知的,剩下就是求1~(x-lowbit(x))的和,这时s[x-lowbit(x)]又变成了已知,因此和单点修改一样,区间求和也是一个递归的过程,代码依然很短:
int srch(int x){ int sum=0; while(x){ sum+=s[x]; x-=lowbit(x); } return sum; }
模板
洛谷P3374#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 500000
using namespace std;
int s[MAXN+5];
int next;
int n,m;
int lowbit(int x){ return x&(-x); }
void nsrt(int x,int w){ while (x<=n){ s[x]+=w; x+=lowbit(x); } }
int srch(int x){ int sum=0; while(x){ sum+=s[x]; x-=lowbit(x); } return sum; }
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++){
int x;
scanf("%d",&x);
nsrt(i,x);
}
for (int i=1;i<=m;i++){
int flag;
scanf("%d",&flag);
if (flag==1){
int x,k;
scanf("%d%d",&x,&k);
nsrt(x,k);
}
else{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",srch(y)-srch(x-1));
}
}
return 0;
}
区间修改区间求和
百度上说区间修改时只能求单点值,但是貌似区间也是可以的。。。因为博主比较懒中间过程比较烦,这里博主就不写了,
引用一下某大佬的blog
模板:
洛谷P3368#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN 500000
using namespace std;
int s[MAXN+5][3];
int n,m;
int lowbit(int x){ return x&(-x); }
void nsrt(int l,int r,int w){
int x;
r++;
x=l;
while (x<=n){
s[x][1]+=w;
s[x][2]+=w*(l-1);
x+=lowbit(x);
}
x=r;
while (x<=n){
s[x][1]-=w;
s[x][2]-=w*(r-1);
x+=lowbit(x);
}
}
int srch(int x){
int now=x,sum=0;
while (now){
sum+=x*s[now][1]-s[now][2];
now-=lowbit(now);
}
return sum;
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++){
int x;
scanf("%d",&x);
nsrt(i,i,x);
}
for (int i=1;i<=m;i++){
int flag;
scanf("%d",&flag);
if (flag==1){
int x,y,k;
scanf("%d%d%d",&x,&y,&k);
nsrt(x,y,k);
}
else{
int x;
scanf("%d",&x);
printf("%d\n",srch(x)-srch(x-1));
}
}
return 0;
}
如果嫌弃博主的文章可以看看这位大佬的blog
相关文章推荐
- 洛谷P3374 【模板】树状数组 1(CDQ分治)
- 洛谷P3374(树状数组)
- 洛谷 P3368 树状数组【模板2】
- 洛谷——P3374 【模板】树状数组 1
- 洛谷 P3374 【模板】树状数组 1
- 洛谷 P3374【模板】树状数组 1
- 树状数组 洛谷P3616 富金森林公园
- 【AC自动机】【树状数组】【dfs序】洛谷 P2414 [NOI2011]阿狸的打字机 题解
- 【洛谷】线段树 树状数组区间修改区间查询
- 树状数组 二维偏序【洛谷P3431】 [POI2005]AUT-The Bus
- 洛谷 P3372 线段树 1(树状数组做法)
- 洛谷 3380 【模板】二逼平衡树(树状数组套权值线段树)
- 树状数组简介
- P3374 【模板】树状数组 1
- 洛谷[P3616] 富金森林公园【数据结构】【线段树】【树状数组】
- 洛谷 P3616 富金森林公园 [树状数组]
- 洛谷 3368 【模(mú)板】树状数组 2
- 【洛谷3368】树状数组 2 树状数组+差分
- [BZOJ4889][洛谷P3759][TJOI2017]不勤劳的图书管理员 分块+树状数组
- [NOIP2013提高&洛谷P1966]火柴排队 题解(树状数组求逆序对)