区间修改区间求和的树状数组
Posted logeadd
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了区间修改区间求和的树状数组相关的知识,希望对你有一定的参考价值。
好像树状数组虽然常数小,编程简单,可是资瓷的操作有限,
普通的树状数组只资瓷单点修改和区间查询,首先要将其升级为区间修改
我们利用差分来进行
定义差分数组b[i]=a[i]-a[i-1]
这样$ a[j]=\sum_{i=1}^jb[i] $
这样我们只要用树状数组维护一下b[i]的前缀和就好了
修改区间[l,r]时我们只需要add(l,x)和add(r+1,-x)就好了
就是一个树状数组资瓷区间修改和单点查询的模板
但是这还不够
我们还要区间求和
我们来看
$ \sum_{i=1}^ja[i]=\sum_{i=1}^j\sum_{k=1}^ib[k] $
我们将上面的式子可以写成
$ \sum_{i=1}^ja[i]=\sum_{i=1}^jb[i]*(j-i+1) $
但是上面的式子还是不好维护
我们将其化简为
$ \sum_{i=1}^ja[i]=j*\sum_{i=1}^jb[i]-\sum_{i=1}^jb[i]*(i-1) $(自己推一下,验证一下)
这样我们就可以利用两个树状数组完成区间修改,查询的操作
用两颗树状数组A,B,A维护b[i],B维护b[i]*(i-1)
我们可以很容易得到查询结果就是A(r)*r-B(r)-A(l-1)*(l-1)+B(l-1)
关于修改操作
我们按之前的讨论维护即可
附上代码
# include<iostream> # include<cstdio> # include<cmath> # include<algorithm> const int mn = 100005; int n,m; struct Binary_tree{ long long tr[mn]; void add(int i,long long x) { while(i<=n) { tr[i]+=x; i+=i&-i; } } long long qsum(int i) { long long ret=0; while(i>0) { ret+=tr[i]; i-=i&-i; } return ret; } }A,B; long long a[mn]; int main() { int opt,x,y,z; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); } for(int i=1;i<=n;i++) { A.add(i,a[i]-a[i-1]); B.add(i,(a[i]-a[i-1])*(i-1)); } for(int i=1;i<=m;i++) { scanf("%d%d%d",&opt,&x,&y); if(opt==1) { scanf("%d",&z); A.add(x,z); A.add(y+1,-z); B.add(x,z*(x-1)); B.add(y+1,-z*y); } else { printf("%lld\n",(A.qsum(y)*y-(x-1)*A.qsum(x-1))-B.qsum(y)+B.qsum(x-1)); } } return 0; }
以上是关于区间修改区间求和的树状数组的主要内容,如果未能解决你的问题,请参考以下文章