树状数组区间修改+查询

【前言】

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

【实现】

首先明确一下,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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值