数据结构,是OIer们永远没法避免的一个难关,而简单数据结构,则是这一切冒险的开始。html
下面咱们就从最简单的树状数组开始学习简单数据结构吧!ios
概念重要性:较重要数组
概念复杂性:较低数据结构
概念:树状数组函数
讲解连接:树状数组学习
理解:lowbit(x)能够计算出以x为右端点的由t[x]存储的数据和所覆盖的区间宽度(从x-lowbit(x)+1到x)spa
将数据输入后构建树状数组(因为t数组默认初始化为0,因此能够直接使用修改的Add函数对t数组对应位置进行修改)指针
每次修改(插入)完一个元素后,都应该对其后求和时包含了它的元素进行修改code
查询时则经过跳跃查询的方式查询从1到pos的区间和(能够经过前缀和求区间和的作法来求从s到e的区间和)htm
例题:
[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倍);缺点则在于原理复杂(晦涩难懂),且功能较为局限.
(可是用来卡常数却是还不错)