树状数组(初识)

参考:树状数组

简述:一个用来求数组列前缀和的数据结构,可以在O( log(n) )的时间内得到数列的任意前缀和,同样以相同时间对某项加一个常数,是一个很强大的数据结构。

认识:有段时间一直以为树状数组就是普通的数组,只不过可以用它来存储二叉查找树,后来才知道这货其实是一个很难的东西。不过说难,其实它也是一个蛮简单、蛮好入手的一个东西——关键代码相当简洁,只是有点难以理解,但是,用一个图就可以表达清楚了:

PS:这里盗图一用(来源

如果还没有清楚,很正常,来看看一个具体的例子:

有数组a,a = {1, 2, 3, 4, 5, 6, 7, 8},然后有一个bit数组,储存树状数组用的,它的值如下:

bit[1] = a[1] ;

bit[2] = a[1]+a[2] ;

bit[3] = a[3];

bit[4] = a[1]+a[2]+a[3]+a[4] ;

bit[5] = a[5] ;

bit[6] = a[5]+a[6];

bit[7] = a[7] ;

bit[8] = a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8] .

然后可以发现,上面这个数组的各个值,又有一个规律:

bit[1] = a[1] ;

bit[2] = a[2]+bit[1] ;

bit[3] = a[3];

bit[4] = a[4]+bit[2]+bit[3] ;

bit[5] = a[5] ;

bit[6] = a[6]+bit[5] ;

bit[7] = a[7] ;

bit[8] = a[8]+bit[4]+bit[6]+bit[7] .

把上面的八个式子跟那个图对应着看,有没有发现什么神奇的东西?

嗯,其实那个图,想表达的就是那几个式子,从那几个例子我们其实可以隐隐约约看出bit数组的规律,但是这东西怎么用程序来表达呢?

1、首先得有一个预备函数lowbit( int ),返回参数转为二进制后最后一个1的位置代表的数值:

int lowbit(int num){
    return num&(-num);
}

这里用了一些位运算操作。

2、然后就是bit数组的构建了,等于啥呢?

void build()
{
    for(int i=1;i<=n;i++)
    {
        bit[i]=a[i];
        for(int j=i-1;j>i-lowbit(i);j-=lowbit(j))
            bit[i]+=bit[j];
    }
}

这段代码表达的就是那个图,至于为啥,读者自己思考吧,会有很大收获哦!

3、接下来看看这东西怎么做到高速求前缀和的:

int sum(int index)
{
    int res=0;
    for(int i=index;i>0;i-=lowbit(i))
        res+=bit[i];
    return res;
}

如果你理解了上面的build函数,那这段代码必然是秒懂,这里我就举个例子吧:

还是上面那个8位数组,用sum数组表示a数组的前缀和:

sum[1] = bit[1] ;

sum[2] = bit[2] ;

sum[3] = bit[2]+bit[3] ;

sum[4] = bit[4] ;

sum[5] = bit[4]+bit[5] ;

sum[6] = bit[4]+bit[6] ;

sum[7] = bit[4]+bit[6]+bit[7] ;

sum[8] = bit[8] .

参照代码想一下下……

4、修改怎么破,如果给a[i]增加Δ:

直接上代码吧:

void add(int index,int delta){
    for(int i=index;i<=n;i+=lowbit(i))
        bit[i]+=delta;
}

看懂了2,就看懂了3、4!

总结:

1、优美的数据结构,不能再美!

2、用二进制思考问题!

时间: 2024-08-25 17:23:21

树状数组(初识)的相关文章

【初识——树状数组】 区间求最值

说树状数组其实是一个索引表,但是是一个特殊的,树状的索引表,它利用了二进制的一些特性. 就区间求和的要求来说: 首先我们用a[]数组来存储原始数据.然后在a[]之上构造c[]数组来作为树状数组. 如图 这个图表示,当i为奇数时,c[i]中保存的都是a[i]本身.然后,c[2]中保存了a[1], a[2],共2个,c[4]中保存的是a[1], a[2], a[3], a[4],c[6]又是保存两个,c[5]和c[6].c[8]保存8个,c[1], c[2], c[3], c[4], c[5], c

初识树状数组

描述 Description 输入一个数列A1,A2-.An(1<=N<=100000),在数列上进行M(1<=M<=100000)次操作,操作有以下两种: (1) 格式为C I X,其中C为字符"C",I和X(1<=I<=N,|X|<=10000)都是整数,表示把把a[I]改为X (2) 格式为Q L R,其中Q为字符"Q",L和R表示询问区间为[L,R](1<=L<=R<=N),表示询问A[L]+-+A

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

(POJ 3067) Japan (慢慢熟悉的树状数组)

Japan Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 29295   Accepted: 7902 Description Japan plans to welcome the ACM ICPC World Finals and a lot of roads must be built for the venue. Japan is tall island with N cities on the East coas

【二维树状数组】See you~

https://www.bnuoj.com/v3/contest_show.php?cid=9148#problem/F [题意] 给定一个矩阵,每个格子的初始值为1.现在可以对矩阵有四种操作: A x y n1 :给格点(x,y)的值加n1 D x y n1: 给格点(x,y)的值减n1,如果现在格点的值不够n1,把格点置0 M x1 y1 x2 y2:(x1,y1)移动给(x2,y2)n1个 S x1 y1 x2 y2 查询子矩阵的和 [思路] 当然是二维树状数组 但是一定要注意:lowbi

Vijos P1066 弱弱的战壕【多解,线段树,暴力,树状数组】

弱弱的战壕 描述 永恒和mx正在玩一个即时战略游戏,名字嘛~~~~~~恕本人记性不好,忘了-_-b. mx在他的基地附近建立了n个战壕,每个战壕都是一个独立的作战单位,射程可以达到无限(“mx不赢定了?!?”永恒[email protected][email protected]). 但是,战壕有一个弱点,就是只能攻击它的左下方,说白了就是横纵坐标都不大于它的点(mx:“我的战壕为什么这么菜”ToT).这样,永恒就可以从别的地方进攻摧毁战壕,从而消灭mx的部队. 战壕都有一个保护范围,同它的攻击

CF 313 DIV2 B 树状数组

http://codeforces.com/contest/313/problem/B 题目大意 给一个区间,问你这个区间里面有几个连续相同的字符. 思路: 表示个人用树状数组来写的...了解了树状数组的本质就行了. 当然用sum[r]-sum[l]也是可以的

Hdu5032 极角排序+树状数组

题目链接 思路:参考了题解.对询问进行极角排序,然后用树状数组维护一下前缀和即可. /* ID: onlyazh1 LANG: C++ TASK: test */ #include<bits/stdc++.h> using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 typedef long long ll; const int maxn=1010; const int maxm=10

Curious Robin Hood(树状数组+线段树)

1112 - Curious Robin Hood    PDF (English) Statistics Forum Time Limit: 1 second(s) Memory Limit: 64 MB Robin Hood likes to loot rich people since he helps the poor people with this money. Instead of keeping all the money together he does another tri