利用差分实现的树状数组区间修改 区间求和

最开始和很不敢相信竟然树状数组还可以区间修改,既然常数这么小,而且好写易调的树状数组可以写区间修改了,那岂不美滋滋?


所以我在网上查了查做法,竟然学会了???
Orz http://blog.csdn.net/qq_21841245/article/details/43956633
这篇博客给了我很大帮助
然后我们可以这样思考,如果现在是区间修改,单点查询,那么我们是不是可以差分一下,在区间的开始和结束进行差分标记,然后用树状数组维护差分数组的前缀和即可,然后询问的时候只需要询问到到这个点的差分数组的前缀和再加上这个点的值就可以吧
那么如果是区间修改区间查询呢?我们仍然维护前缀和,也是差分数组的前缀和,不过维护的方式有一些变化,我们进行如下的公式推导

sum[i]=j=1ia[j]+delta[1]i+delta[2](i1)+...+delta[i]1

=j=1ia[j]+j=1idelta[j](ij+1)

=j=1ia[j]+j=1idelta[j]j=1idelta[j]j

所以我们只需要维护后面的两个式子就行了,分别用两个树状数组维护delta[x]的前缀和和delta[x]*x的前缀和,而前面的那个式子我们可以预处理出来
所以我也写了那位博主的那道题,果然奇快233

#include
#include
#include
#include
#include
#include
#include
#include
#include
const int MAXN=200000*4;
using namespace std;
int lowbit(int x){return x&(-x);}
int n,Q,opt,x,y;
long long A[MAXN],C[MAXN],sum[MAXN],z;
void modifyA(int x,int val){
    while(x<=n){
        A[x]+=val;
        x+=lowbit(x);
    }
}
void modifyC(int x,long long val){
    while(x<=n){
        C[x]+=val;
        x+=lowbit(x);
    }
}
void update(int from,int to,long long val){
    modifyA(from,val);
    modifyA(to+1,-val);
    modifyC(from,val*from);
    modifyC(to+1,-val*(to+1));
}
long long queryA(int loc){
    long long ans=0;
    while(loc){
        ans+=A[loc];
        loc-=lowbit(loc);
    }
    return ans;
}
long long queryC(int loc){
    long long ans=0;
    while(loc){
        ans+=C[loc];
        loc-=lowbit(loc);
    }
    return ans;
}
long long query(int loc){
    return sum[loc]+(loc+1)*queryA(loc)-queryC(loc);
}
long long query(int from,int to){
    return query(to)-query(from-1);
}
int main(){
    scanf("%d",&n);
    for(register int i=1;i<=n;i++)scanf("%d",&sum[i]),sum[i]+=sum[i-1];
    scanf("%d",&Q);
    while(Q--){
        scanf("%d%d%d",&opt,&x,&y);
        if(opt==1){
            scanf("%lld",&z);
            update(x,y,z);
        }else{
            printf("%lld\n",query(x,y));
        }
    }
    return 0;
}

利用差分实现的树状数组区间修改 区间求和_第1张图片

你可能感兴趣的:(树状数组,算法讲解)