您的位置:首页 > 其它

树状数组简介(洛谷P3368、P3374)

2017-08-04 16:45 405 查看

算法用途

树状数组是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和。

虽然树状数组的用途可以完全被线段树所替代,而且线段树所能做的比树状数组多得多,但是树状数组的常数是远远小于线段树的。因此当你写线段树被卡常时可以试试使用树状数组。

而且树状数组的代码很短哦!

算法思想

开一个数组s,其中s[i]的值存的是a[i-lowbit(i)]~a[i]这段区间的和。然后进行一系列操作。

差不多是这样一个图(这里是c数组):



像不像一棵树啊#(滑稽)

关于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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: