【入门向】树状数组概念理解与例题实现
数据结构,是OIer们永远无法避免的一个难关,而简单数据结构,则是这一切冒险的开始。
下面我们就从最简单的树状数组开始学习简单数据结构吧!
概念重要性:较重要
概念复杂性:较低
概念:树状数组
讲解链接:树状数组
理解:lowbit(x)可以计算出以x为右端点的由t[x]存储的数据和所覆盖的区间宽度(从x-lowbit(x)+1到x)
将数据输入后构建树状数组(由于t数组默认初始化为0,所以可以直接使用修改的Add函数对t数组对应位置进行修改)
每次修改(插入)完一个元素后,都应该对其后求和时包含了它的元素进行修改
查询时则通过跳跃查询的方式查询从1到pos的区间和(可以通过前缀和求区间和的做法来求从s到e的区间和)
例题:
[Luogu P3368] (模板)数状数组2 //PS:这题需要使用差分思想,对思维有一定锻炼作用
具体示例:
P3374:
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define lowbit(x) x&(-x)//大名鼎鼎的lowbit(函数) const int MAXN = 600000; using namespace std; int t[MAXN],size; void Add(int pos,int n)//给pos位置上的数加上n { while(pos<=size)//向上循环更新包括pos节点的各节点的值(每次向上跳lowbit(x)) { t[pos]+=n;//更新当前指向的节点的值 pos+=lowbit(pos);//移动"指针"(然而博主并不会写指针...) } } long long Qry(int pos)//查询从1到pos的所有数之和 { long long sum=0; while(pos)//向下循环求和 { sum+=t[pos];//将当前节点的值加入总和中 pos-=lowbit(pos);//跳过当前pos位置所求出和的部分,对之前还未求和的数字进行处理 } return sum; } int main() { int n; ios::sync_with_stdio(false);//关闭流同步... memset(t,0,sizeof(t));//初始化数组 cin>>size>>n;//输入数组大小和操作次数 int num; for(int i=1;i<=size;i++) { cin>>num;//输入第i位的值 Add(i,num);//将num加到第i位上,并向上更新所有包括i的节点 } for(int i=1;i<=n;i++)//处理n次操作 { int s,e,ops; cin>>ops>>s>>e;//ops=1 ==> 将s处的值加上e ; ops=2 ==> 查询从s到e的值(闭区间[s,e]) if(ops==1) Add(s,e);//更新(修改) else cout<<Qry(e)-Qry(s-1)<<endl; //查询([s,e]闭区间可以表示为从e到1与从s-1到1的和之差) } return 0; }
P3668(无注释)
#include<cstdio> #include<iostream> #include<algorithm> #define lowbit(x) ((x!=0)?x&(-x):1) const int MAXN = 600000; using namespace std; int d[MAXN]={0},num[MAXN]={0},n; int Add(int pos,int num) { while(pos<=n) { d[pos]+=num; pos+=lowbit(pos); } } int query(int pos) { int sum=0; while(pos) { sum+=d[pos]; pos-=lowbit(pos); } return sum; } int main() { int m,x,y,k,opr; scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); } for(int i=1;i<=m;i++) { scanf("%d",&opr); if(opr==1) { scanf("%d %d %d",&x,&y,&k); Add(x,k); Add(y+1,-k); } else { scanf("%d",&x); printf("%d\n",num[x]+query(x)); } } return 0; }
还有大量的树状数组的经典例题,这里便不一一列举了 (话说NOIP2017 [就是那场D1T1是结论题的傻×比赛(差点被逼到退役...)] D2最后一题 正解即是树状数组...)
重点:树状数组只适用于维护有区间可减性的数据(如求和/逆序对等问题),如果维护的数据不具备这种性质,一般使用这种方法便是错误的,这是我们就需要求助于其它的数据结构啦...
树状数组对于线段树的优点在于代码量少,不易犯错(对新手较为友善),同时速度比线段树快,空间占用量比线段树少(1倍VS4倍);缺点则在于原理复杂(晦涩难懂),且功能较为局限.
(但是用来卡常数倒是还不错)

浙公网安备 33010602011771号