树状数组简介(洛谷P3368、P3374)
来源:互联网 发布:安倍经济学知乎 编辑:程序博客网 时间:2024/06/03 22:55
算法用途
树状数组是一个查询和修改复杂度都为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 500000using 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
- 树状数组简介(洛谷P3368、P3374)
- 洛谷P3368 树状数组
- 洛谷P3374 树状数组
- 【模板】树状数组 洛谷P3374
- 树状数组 【模板2】洛谷 P3368
- 洛谷 P3368 【模板】树状数组 2
- 洛谷P3368 【模板】树状数组 2
- 洛谷P3368 【模板】树状数组 2
- 洛谷 P3374 【模板】树状数组 1
- 洛谷 P3374【模板】树状数组 1
- 洛谷P3374 [模板]树状数组1
- 洛谷P3374 【模板】树状数组 1
- 【洛谷】 P3374 【模板】树状数组 1
- 洛谷 P3374 【模板】树状数组 1
- P3374 树状数组 1
- 【模板】树状数组 区间修改,区间求和 (模板题:洛谷P3368树状数组2)
- 【模板】树状数组 单点修改,区间求和 (模板题:洛谷P3374树状数组1)
- 树状数组,洛谷P3374 【模板】树状数组 1
- 线程池
- 51nod 1315 合法整数集
- 关于synchronized
- linux下搭建简单的git服务器测试1
- 实现响应式布局的方式-CSS3实现
- 树状数组简介(洛谷P3368、P3374)
- Color Length UVA
- Windows 下部署Jenkins相关插件总结
- 自动打包ipa文件,上传fir.im托管平台
- 全志R16平台的androidM系统下调通GC0308(分色排版)
- HDU6070(二分+线段树区间更新)
- UVA 489
- [ASP.NET]视频总结
- LinearLayout 不能转换为 Button 另一种情况