复习笔记-树状数组(二)
树状数组(一)
略微进阶的操作
在树状数组(一)中,身为懒得打线段树(有时候都未必会打)的蒟蒻,我安利了一波树状数组,并且介绍了区间查询和单点修改的基本操作。那么,对基础的树状数组进行一些修改,结合差分,就可以同时进行区间修改和单点查询。
差分数组
存储方式
差分数组是相较于前缀和的一种截然相反的存储方式,前缀和的每个数表示原数组的前缀和,而差分数组的每个数表示原数组这个数和上一个数的差,也就是说
C1=A1
Cx=Ax-Ax-1
那么容易推出
C1+C2+...+Cm=Am
也就是说,差分数组元素的前缀和等于原数组该元素的值,这就是我说它与前缀和截然相反的第一个原因。
优势
差分数组由于每个数存的是与上一个数的差,可以理解成存的是数据的波动。那么,需要区间修改时,也就相当于区间内波动不变,区间左端波动上升,区间右端波动下降。这样就可以用O(1)复杂度完成区间修改。也就是说
如果把第x到第y加上k,只需要把差分数组第x个元素+k,第y+1个元素-k,就相当于完成了区间修改,只需要修改两处。
这是差分数组的修改的基础代码
1 void add(int x,int y,int k){ 2 c[x] += k; 3 c[y + 1] -= k; 4 }
综上,差分数组可以O(1)完成区间修改。由于前缀和可以O(1)完成区间查询,这就是我说这两者相反的第二个原因
差分数组与树状数组的结合
上面我叭叭了半天差分数组不是瞎叭叭,而是要与树状数组进行结合。
差分数组与树状数组特点对比:
————————————————————————————
差分数组:前缀和表示原数,可以用两次单点修改完成区间修改
树状数组:可以快速进行单点修改和查询前缀和
————————————————————————————
对比以上,不难想到,如果对差分数组建立树状数组,就可以对差分数组进行前缀和查询达到单点查询的目的,运用两次单点修改来完成区间修改的目的
例题与程序实现
这道模板题要求完成区间修改和单点查询
那么利用差分数组与树状数组的结合,就可以这样完成
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,l,r,x; 4 long long c[500010],k,a; 5 int lowbit(int x){ 6 return x&(-x); 7 } 8 void add(int x,long long y){ 9 while(x<=n){ 10 c[x]+=y; 11 x+=lowbit(x); 12 } 13 } 14 long long sum(int x){ 15 long long cnt=0; 16 while(x){ 17 cnt+=c[x]; 18 x-=lowbit(x); 19 } 20 return cnt; 21 } 22 int main(){ 23 scanf("%d%d",&n,&m); 24 long long now=0; 25 for(int i=1;i<=n;i++){ 26 scanf("%lld",&a); 27 add(i,a-now); 28 now=a; 29 } 30 while(m--){ 31 scanf("%d",&x); 32 if(x==1){ 33 scanf("%d%d%lld",&l,&r,&k); 34 add(l,k); 35 add(r+1,-k); 36 } 37 else{ 38 int s; 39 scanf("%d",&s); 40 printf("%lld\n",sum(s)); 41 } 42 } 43 return 0; 44 }