树状数组区间修改+查询

来源:互联网 发布:手机日记本软件 编辑:程序博客网 时间:2024/05/01 02:59

【前言】

树状数组可以处理单点修改,区间求和的操作,这里就不再赘述了。现在,我们要考虑用树状数组实现区间加,区间求和的操作。由于树状数组常数较小,代码简短,可以在一些场合代替线段树。

【实现】

首先明确一下,query(c,i)表示求c数组的前缀和。

定义要维护的序列为a[],对a[]进行一次差分,得到c[]。
即c[i]=a[i]-a[i-1],显然c[1]+c[2]+……+c[i]=a[i]

如何维护c[]?
当对区间[l,r]进行加w的操作时,c[l]+=w;c[r+1]-=w;
这样就成功维护了c[]。事实上,此时我们已经可以实现区间加,单点求和的操作了。

接下来考虑区间求和

a[1]+a[2]+……+a[i]=(c[1])+(c[1]+c[2])+……+(c[1]+c[2]+……+c[i])=c[1]*i+c[2]*(i-1)+……+c[i]=(c[1]+c[2]+……+c[i])*i-(c[1]*0+c[2]*1+……+c[i]*(i-1))

我们就可以定义cc[i]=c[i]*(i-1)
答案就是query(c,i)*n-query(cc,i)

如何维护cc[]?
当对区间[l,r]进行加w的操作时,cc[l]+=w*(l-1);cc[r+1]+=-w*r
这样就维护了cc[],也就成功解决了所有问题。

贴上代码:

#include<cstdio>#define LL long long#define lowbit(x) ((x)&-(x))const int maxn=200005;int n,q;LL c[maxn],cc[maxn];inline int red(){    int tot=0,f=1;char ch=getchar();    while (ch<'0'||'9'<ch) {if (ch=='-') f=-f;ch=getchar();}    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();    return tot*f;}void ist(LL *tre,int x,LL w){    for (int i=x;i<=n;i+=lowbit(i)) tre[i]+=w;}LL qry(LL *tre,int x){    LL res=0;    for (int i=x;i;i-=lowbit(i)) res+=tre[i];    return res;}void add(int l,int r,LL w){    ist(c,l,w);ist(c,r+1,-w);ist(cc,l,w*(l-1)),ist(cc,r+1,-w*r);}LL getsum(int x){return qry(c,x)*x-qry(cc,x);}int main(){    n=red();    for (int i=1;i<=n;i++){        LL x=red();        add(i,i,x);    }    q=red();    while (q--)     if (red()==1){        int l=red(),r=red();LL w=red();        add(l,r,w);     }else{        int l=red(),r=red();        printf("%lld\n",getsum(r)-getsum(l-1));     }    return 0;}
3 0