树状数组区间更新与查询

设原数组为: a [ 1 ] , a [ 2 ] , a [ 3 ] . . . a [ n ] a[1],a[2],a[3]...a[n]
记录其查分序列: c [ 1 ] = a [ 1 ] , c [ 2 ] = a [ 2 ] a [ 1 ] , c [ 3 ] = a [ 3 ] a [ 2 ] . . . c [ n ] = a [ n ] a [ n 1 ] c[1]=a[1],c[2]=a[2]-a[1],c[3]=a[3]-a[2]...c[n]=a[n]-a[n-1]
此时有: a [ i ] = c [ 1 ] + c [ 2 ] + . . . c [ i ] a[i]=c[1]+c[2]+...c[i]
那么原数组的前缀和变为: i = 1 k a [ i ] = k c [ 1 ] + ( k 1 ) c [ 2 ] + . . . c [ k ] \sum_{i=1}^ka[i]=kc[1]+(k-1)c[2]+...c[k]
维护一个辅助数组cc: c c [ i ] = ( i 1 ) c [ i ] cc[i]=(i-1)c[i]
那么原数组前缀和变成: i = 1 k a [ i ] = k i = 1 k c [ i ] i = 1 k c c [ i ] \sum_{i=1}^ka[i]=k\sum_{i=1}^kc[i]-\sum_{i=1}^kcc[i]
区间查询就可以实现了,接下来是区间更新

如果我需要对[L,R]区间的数加上V,只需要利用查分序列修改两端的值即可 c [ L ] + = V , c [ R + 1 ] = V , c c [ L ] + = ( L 1 ) V , c c [ R + 1 ] = R V c[L]+=V,c[R+1]-=V,cc[L]+=(L-1)V,cc[R+1]-=RV


例题

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define debug(i) printf("# %d\n",i)
#define pill pair<int,int>
const LL mod = 998244353;
const int N = 200005;

LL a[N];
LL c[N],cc[N];

void add(LL* tr,int pos,LL v,int n){
    while(pos<=n)tr[pos]+=v,pos+=pos&-pos;
}

LL query(LL* tr,int pos){
    LL ans=0;
    while(pos)ans+=tr[pos],pos-=pos&-pos;
    return ans;
}

int main(){
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",a+i);
    }
    for(int i=1;i<=n;i++)add(c,i,a[i]-a[i-1],n),add(cc,i,(i-1)*(a[i]-a[i-1]),n);

    int q;scanf("%d",&q);
    while(q--){
        int op,l,r;scanf("%d%d%d",&op,&l,&r);
        if(op==1){
            LL v;
            scanf("%lld",&v);
            add(c,l,v,n);
            add(c,r+1,-v,n);
            add(cc,l,(l-1)*v,n);
            add(cc,r+1,-r*v,n);
        }
        else{
            printf("%lld\n",r*query(c,r)-query(cc,r)-((l-1)*query(c,l-1)-query(cc,l-1)));
        }
    }
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/85217673