树状数组入门笔记

来源:互联网 发布:一句话介绍自己 java 编辑:程序博客网 时间:2024/06/06 08:39

网上有很多关于树状数组的博客,有很多也讲得很好。
但我感觉对于树状数组,自己始终只能去套模板,所以打算自己写一篇博客,梳理梳理树状数组的用法。

首先上一篇博客。树状数组入门
感觉很无敌,入门足够用了。
我们用C[] 数组来储存树状数组中的信息。
这里写图片描述
1=(001) C[1]=A[1];
2=(010) C[2]=A[1]+A[2];
3=(011) C[3]=A[3];
4=(100) C[4]=A[1]+A[2]+A[3]+A[4];
5=(101) C[5]=A[5];
6=(110) C[6]=A[5]+A[6];
7=(111) C[7]=A[7];
8=(1000) C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];
找规律发现,C[i]所代表的元素是原数组中以i结尾的,连续的,2^k 个元素。(这里的k其实可以代表这个元素在树中的高度)
所以可以轻松发现这其中有前缀和的性质。
例如前3(11)个元素 就是C[3(11)]+C[2(10)]
再例如前6(110)个元素 就是C[110]+C[100]
每次去除一个末尾1 直至下标为0。
那么 利用前缀和的思想,我们就可以很轻松的求出任意连续区间内的数字的和。
再然后就是修改操作。 每修改一个元素,我们都要修改与之相关的所有元素。
由于树的最高高度为logn层,每层只修改一个元素,所以最多修改logn个元素,因此修改操作的复杂度为O(logn)而普通的前缀和修改一个元素需要O(n)的时间。
同理,对于查询操作,其复杂度也为O(logn).
树状数组模板如下

int lowbit(int x){return x&(-x);}void add(int i,int val){    for(;i<=n;i+=lowbit(i))        bit[i]+=val;}int query(int i){    int ans=0;    for(;i>=1;i-=lowbit(i))        ans+=bit[i];    return ans;}

然后树状数组很容易推广到二维,相当于n个树状树状数组加起来。

const int MAX=2555;int seed=6666;int n,m,q;int lowbit(int x){    return x&(-x);}int c[MAX][MAX];int sum(int i,int j){    int tempj,sums=0;    while(i>0)    {        tempj=j;        while(tempj>0)        {            sums+=c[i][tempj];            tempj-=lowbit(tempj);        }        i-=lowbit(i);    }    return sums;}void update(int x,int y,long long num){    for(int i=x;i<=n;i+=lowbit(i))    {        for(int j=y;j<=m;j+=lowbit(j))        {            c[i][j]+=num;        }    }}

未完待续。

原创粉丝点击