美文网首页线段树
三种一维树状数组

三种一维树状数组

作者: the_Miracle | 来源:发表于2018-04-30 14:43 被阅读38次

单点修改+区间查询

最基本的树状数组

树状数组入门

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

#include<cstdio>
#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<cstdio>
#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<cstdio>
#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;
} 

(简书的代码高亮有毒)

相关文章

  • 数据结构(二)

    树状数组 POJ 1990: MooFest关于x坐标建立树状数组。先按照v值升序排序,逐个添加到树状数组里。每次...

  • 三种一维树状数组

    单点修改+区间查询 最基本的树状数组 树状数组入门 模板(洛谷P3374 【模板】树状数组1) 区间修改+单点查询...

  • 求解连续子数组和全解析-常规解法VS树状数组!

    本文将介绍几求解数组前缀和和连续子数组和的三种方法,分别是遍历法、辅助数组法、树状数组法。 先来定义我们的问题,假...

  • 树状结构转一维数组

    树状结构转数组方法 声明树状对象

  • 树状数组

    复习一下树状数组 树状数组 一种用于处理单点修改和区间查询的数据结构。树状数组C的定义: C[x] = Sum ...

  • 树状数组模板复习

    树状数组模板复习

  • 树状数组求逆序对原理

    归并排序和树状数组都可以用nlogn的算法做到求出逆序对.但这里着重讲树状数组的原理与求法.树状数组最常用的方面就...

  • 「树状数组」

    题目传送门 poj1990题意: 农夫的N (N∈[1, 20,000])头牛参加了"MooFest"之后有了不...

  • 树状数组

    参见: https://www.cnblogs.com/hsd-/p/6139376.htmlhttps://bl...

  • 树状数组

    created by Dejavu*[完结] 简介树状数组(Binary Indexed Tree(BIT), F...

网友评论

    本文标题:三种一维树状数组

    本文链接:https://www.haomeiwen.com/subject/wewzlftx.html