三种一维树状数组

单点修改+区间查询

最基本的树状数组

树状数组入门

模板(洛谷P3374 【模板】树状数组1)

#include
#define lowbit(a) (a&(-a))
using namespace std;
int BIT[500010],n,m;
inline int getint(){  
    register int mark=1,ret=0;
    register char ch=getchar();  
    while(ch<'0'||ch>'9'){if(ch=='-')mark=-1;ch=getchar();}  
    while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}  
    return mark*ret;   
}
inline void add(int pos,int num){
    while(pos<=n){
        BIT[pos]+=num;
        pos+=lowbit(pos);
    }
}
inline int getsum(int pos){
    register int ret(0);
    while(pos>0){
        ret+=BIT[pos];
        pos-=lowbit(pos);
    }
    return ret;
}
int main(int argc,char **argv){
    n=getint(),m=getint();
    for(register int i(1),tmp;i<=n;++i){
        tmp=getint();
        add(i,tmp);
    }
    register int cmd,x,y;
    while(m--){
        cmd=getint(),x=getint(),y=getint();
        switch(cmd){
            case 1:
                add(x,y);
                break;
            case 2:
                printf("%d\n",getsum(y)-getsum(x-1));
        }
    }
    return 0;
}

区间修改+单点查询

通常区间修改大家都会选择用线段树了,但实际上树状数组也能解决这类问题。

区间修改+单点查询的树状数组用到了差分的思想。什么是差分呢?例如:

int a[]={0,2,3,5,3,8,20,23,56};
int b[]={0,2,1,2,-2,5,12,3,33};

不难观察,b数组的每一位存的是a每一位与前面一位的差值。这时区间修改就很容易了,例如给a的区间[2,4]加上2:

a[]={0,2,5,7,5,8,20,23,56};
int b[]={0,2,3,2,-2,3,12,3,33};

可以看到b只变化了两位,也就是b[3]:1->3和b[5]:5->3。这样只需要修改两位就可以实现区间修改,但单点查询时间是O(n),这显然也就很不好,这时就想到树状数组实现。

代码中的三个函数均不改变。但是主函数中:

  1. 输入模块变为了存查值
  2. 修改模块对修改区间后的区间造成了影响,所以此时再减去增加的数
  3. 查询函数并没有改变,但它本身是用来求前n项和的,由前面所讲的差分可以知道它现在求的实际上是第n项

这样就可以实现区间修改和单点查询了

模板(洛谷P3368 【模板】树状数组2)

#include
#define lowbit(a) (a&(-a))
using namespace std;
int BIT[500010],n,m;
inline int getint(){  
    register int mark=1,ret=0;
    register char ch=getchar();  
    while(ch<'0'||ch>'9'){if(ch=='-')mark=-1;ch=getchar();}  
    while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}  
    return mark*ret;   
}
inline void add(int pos,int num){
    while(pos<=n){
        BIT[pos]+=num;
        pos+=lowbit(pos);
    }
}
inline int getnum(int pos){
    register int ret(0);
    while(pos>0){
        ret+=BIT[pos];
        pos-=lowbit(pos);
    }
    return ret;
}
int main(int argc,char **argv){
    n=getint(),m=getint();
    register int x(0),y;
    for(register int i(1);i<=n;++i){
        y=getint();
        add(i,y-x);
        x=y;
    }
    register int cmd,k;
    while(m--){
        cmd=getint();
        switch(cmd){
            case 1:
                x=getint(),y=getint(),k=getint();
                add(x,k);
                add(y+1,-k);
                break;
            case 2:
                x=getint();
                printf("%d\n",getnum(x));
        }
    }
    return 0;
} 

区间修改+区间查询

学到这里我整匹马都惊了

前面讲到用差分来维护树状数组,这里思想差不多。

设原数组为a,使得

b[i]=a[i]+a[i-1]

可以得到

S[n]=b[1]+b[2]+···+b[n]
    =a[1]+(a[1]+a[2])+···+(a[1]+a[2]+···+a[n])
    =n*a[1]+(n-1)*a[2]+···+1*a[n]
    =(n+1-1)*a[1]+(n+1-2)*a[2]+···+(n+1-n)*a[n]
    =(n+1)*(a[1]+a[2]+···+a[n])-(1*a[1]+2*a[2]+···+n*a[n])
    =(n+1)*sum(a[i])-sum(i*a[i])

从而只需要求出sum(a[i])sum(i*a[i]) 就可以了,所以我们维护两个数组分别代表 a[i]i*a[i] ,其他的函数全部沿用就可以了

模板(洛谷P3372 【模板】线段树1)

#include
#define lowbit(a) (a&(-a))
using namespace std;
long long BIT1[100010],BIT2[100010];
int n,m;
inline int getint(){  
    register int mark(1),ret=(0);
    register char ch=getchar();  
    while(ch<'0'||ch>'9'){if(ch=='-')mark=-1;ch=getchar();}  
    while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}  
    return mark*ret;   
}
inline long long getll(){
    register long long mark(1),ret(0);
    register char ch=getchar();  
    while(ch<'0'||ch>'9'){if(ch=='-')mark=-1ll;ch=getchar();}  
    while(ch>='0'&&ch<='9'){ret=ret*10ll+ch-'0';ch=getchar();}  
    return mark*ret;
}
inline void add(long long *BIT,int pos,long long num){
    while(pos<=n){
        BIT[pos]+=num;
        pos+=lowbit(pos);
    }
}
inline long long getsum(long long *BIT,int pos){
    register long long ret(0);
    while(pos>0){
        ret+=BIT[pos];
        pos-=lowbit(pos);
    }
    return ret;
}
int main(int argc,char **argv){
    n=getint(),m=getint();
    register long long tmp1,tmp2(0);
    for(register int i(1);i<=n;++i){
        tmp1=getll();
        add(BIT1,i,tmp1-tmp2);
        add(BIT2,i,(long long)i*(tmp1-tmp2));
        tmp2=tmp1;
    }
    register int cmd,x,y;
    register long long k;
    while(m--){
        cmd=getint();
        switch(cmd){
            case 1:
                x=getint(),y=getint(),k=getll();
                add(BIT1,x,k);
                add(BIT1,y+1,-k);
                add(BIT2,x,(long long)x*k);
                add(BIT2,y+1,(long long)(y+1)*(-k));
                break;
            case 2:
                x=getint(),y=getint();
                register long long suml=(long long)x*getsum(BIT1,x-1)-getsum(BIT2,x-1);
                register long long sumr=(long long)(y+1)*getsum(BIT1,y)-getsum(BIT2,y);
                printf("%lld\n",sumr-suml);
        }
    }
    return 0;
} 

(的代码高亮有毒)

你可能感兴趣的:(三种一维树状数组)