树状数组--区间加单点询问

来源:互联网 发布:哪里有im域名 编辑:程序博客网 时间:2024/06/06 01:16

我们知道,线段树用来做单点修改是很方便的,那么,既然这么方便,为什么不能用它来做区间修改呢?好吧,我们来试试看:

首先不难想到要把区间修改转化为前缀修改,即,将把[L,R]这段区间加k,变成把[1,R]加k,再把[1,L1]减去k,这显然是正确的。

那么我们来考虑如何维护这个东西,考虑Ai表示[1,i]这个前缀的增量,那么考虑将一个点x的值增加了k,这个操作会对整个A数组造成什么影响。

显然需要分两种情况讨论:

1i<x

对于这样的Ai,显然它会增加ik

2ix

对于这样的Ai,显然它会增加xk

接下来我们来考虑如何求Ai,显然,我们需要把上述两种情况进行相加得到真正的Ai,考虑第一种情况,现在我们要求的是所有大于ix的增量,所以我们需要求的是后缀和,我们只需要维护一个树状数组,询问所有大于ix的k值,询问的时候把这个值最后i即可。对于第二种情况,我们要求的显然是前缀和,只要直接维护xk的树状数组,每次询问前缀和就可以了。

代码

#include<cstdio>#include<cstring>#include<algorithm>#define maxn 100006#define lowbit(x) (x&-x)#define LL long longusing namespace std;inline char nc(){    static char buf[100000],*i=buf,*j=buf;    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;}inline LL _read(){    char ch=nc();LL sum=0;    while(!(ch>='0'&&ch<='9'))ch=nc();    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();    return sum;}int n,tet;LL a[maxn],f1[maxn],f2[maxn];void put1(int x,LL y){for(;x<=n;x+=lowbit(x))f1[x]+=y;}LL get1(int x){    if(x<0)return 0;    int sum=0;    for(;x;x-=lowbit(x))sum+=f1[x];    return sum;}void put2(int x,LL y){for(;x<=n;x+=lowbit(x))f2[x]+=y;}LL get2(int x){    if(x<0)return 0;    LL sum=0;    for(;x;x-=lowbit(x))sum+=f2[x];    return sum;}void put(int l,int r,LL k){    if(l>1)put1(l-1,-k),put2(l-1,(1-l)*k);    put1(r,k);put2(r,r*k);}LL get(int x){return x*(get1(n)-get1(x))+get2(x);}int main(){    freopen("temp.in","r",stdin);    freopen("temp.out","w",stdout);    n=_read();tet=_read();    for(int i=1;i<=n;i++) a[i]=a[i-1]+_read();    while(tet--){        int k=_read();        if(k==1){            int l=_read(),r=_read(),x=_read();            put(l,r,x);        }else{            int l=_read(),r=_read();            printf("%lld\n",a[r]-a[l-1]+get(r)-get(l-1));        }    }    return 0;}
原创粉丝点击