三种一维树状数组

单点修改+区间查询

最基本的树状数组

树状数组入门

模板(洛谷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;
} 

(简书的代码高亮有毒)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容