树状数组区间修改、区间查询&&区间修改、单点查询

区间修改

任务:
对于数组a[1],a[2],a[3],….a[n],给定区间[l,r]。
要求将a[l]~a[r]的值都加v。
解决:
引入一个差分数组b,b[i]=a[i]-a[i-1](默认a[0]=0)。
比如:
a:1,2,3,4,5
b: 1,1,1,1,1
将区间[2,3]的值都加2
a: 1,4,5,4,5
b: 1,3,1,-1,1
对于a中区间[l,r]都加v,b数组中b[l]+=v,b[r+1]-=v.
故只需对原数组a的差分数组b用树状数组去进行单点修改,就可以快速地对数组a进行区间修改。

单点查询

任务:
对进行了区间修改的数组进行单点查询
解决:
观察差分数组,可以推出:a[i]=b[1]+b[2]+…+b[i]。
故只需求出原数组a的差分数组b的前缀i即可查询a[i]。

区间查询

任务:
对进行了区间修改的数组进行区间查询
解决:
引入第二个数组c,c[i]=i*b[i].称为数组b的i倍数组。
比如:
a:1,2,3,4,5
b:1,1,1,1,1
c:1,2,3,4,5
将区间[2,3]的值都加2后,
a:1,4,5,4,5
b:1,3,1,-1,1
c:1,6,3,-4,5
查询数组a的前3位数的和,即a[1]+a[2]+a[3]=1+4+5=10。
a[1]+a[2]+a[3]=4*(b[1]+b[2]+b[3]) - (c[1]+c[2]+c[3])=4*5 - 10=10.
设sumA表示数组a的前缀和,sumB表示数组b的前缀和,sumC表示数组c的前缀和。
sumA[i]=(i+1)*sumB[i] - sumC[i]。
上述公式其实是可以严格证明的。
证明:
假设要求数组a的前x项和。
因为a[i]=b[1]+b[2]+…+b[i],
a[1]+a[2]+a[3]+…+a[x]=b[1]+(b[1]+b[2])+(b[1]+b[2]+b[3])+…+(b[1]+b[2]+b[3]+…+b[x]).
=b[1]x + b[2](x-1) + b[3](x-2) + b[i](x-i+1) +…+b[1]*x.
所以需要b[x]一次,b[x-1]两次,b[x-2]三次,…,b[i] (x-i+1)次,b[1] x次。

故只需用两个树状数组去维护原数组a的差分数组b的前缀和以及b的i倍数组c的前缀和即可得到原数组a的前缀和。

猜你喜欢

转载自blog.csdn.net/qq_37685156/article/details/80397765