【学习整理】树状数组 区间修改+查询

前言:对于区间修改和区间查询这样的简单问题,打一大堆线段树确实是不划算,所以学习了区间修改+区间修查询的树状数组。

我们定义 a[i] 为原数列,c[i]=a[i]-a[i-1] ,显然  a[n]=\displaystyle{\sum_{i=1}^{n}}c[i]

若想要将区间 [l,r] 的数全部 +v 则只需要将 c[l]+v , c[r+1]-v
即可。

{\sum_{i=1}^{n}}a[i]=(c[1])+(c[1]+c[2])+...+(c[1]+c[2]+...+c[n])

              =n$\times$c[1]+(n-1)$\times$c[2]+...+c[n]

              =n$\times$(c[1]+c[2]+...+c[n])-(0$\times$c[1]+1$\times$c[2]+...+(n-1)$\times$c[n])

所以,我们维护一个数组 c_2[i]=(i-1)$\times$c[i] 

将区间 [l,r] 的数全部 +v 则还需同时将 c_2[l]+v$\times$(i-1) , c_2[r+1]+(-v)$\times$r

结论:{\sum_{i=1}^{n}}a[i]=n$\times${\sum_{i=1}^{n}}c[i]-{\sum_{i=1}^{n}}c_2[i]

 

例题:CODEVS1082 线段树练习3

http://codevs.cn/problem/1082/

 

 

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int n,q;
long long a[200005],c[3][200005];
long long lowbit(long long x){return x&(-x);}
void modify(int x,long long pos,long long v){for(;pos<=n;pos+=lowbit(pos)) c[x][pos]+=v;}
long long query(int x,long long pos){long long ret=0LL;for(;pos;pos-=lowbit(pos)) ret+=c[x][pos];return ret;}
int main()
{
    int i,j,opt;
    long long l,r,v,sum1,sum2;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        modify(1,i,a[i]-a[i-1]);
        modify(2,i,(i-1)*(a[i]-a[i-1]));
    }
    scanf("%d",&q);
    for(i=1;i<=q;i++)
    {
        scanf("%d%lld%lld",&opt,&l,&r);
        if(opt==1)
        {
            scanf("%lld",&v);
            modify(1,l,v);modify(1,r+1,-v);
            modify(2,l,v*(l-1));modify(2,r+1,-v*r); 
        } 
        else
        {
            sum1=(l-1)*query(1,l-1)-query(2,l-1);
            sum2=r*query(1,r)-query(2,r);
            printf("%lld\n",sum2-sum1);
        }
    }
    return 0;
    
}
posted @ 2016-11-12 19:37  yljiang  阅读(527)  评论(0编辑  收藏  举报