Sweety

Practice makes perfect

导航

树状数组

Posted on 2015-04-20 19:24  蓝空  阅读(130)  评论(0编辑  收藏  举报

好久都对这个东西有点陌生,看了一下午,终于能说出点缘由来了。。

从网上找的点资料。。。

一、树状数组是干什么的?

       平常我们会遇到一些对数组进行维护查询的操作,比较常见的如,修改某点的值、求某个区间的和,而这两种恰恰是树状数组的强项!当然,数据规模不大的时候,对于修改某点的值是非常容易的,复杂度是O(1),但是对于求一个区间的和就要扫一遍了,复杂度是O(N),如果实时的对数组进行M次修改或求和,最坏的情况下复杂度是O(M*N),当规模增大后这是划不来的!而树状数组干同样的事复杂度却是O(M*lgN),别小看这个lg,很大的数一lg就很小了,这个学过数学的都知道吧,不需要我说了。

二、原理讲解


给定序列(数列)A,我们设一个数组C满足

C[i] = A[i–2^k+ 1] + … + A[i]

其中,k为i在二进制下末尾0的个数,i从1开始算!

则我们称C为树状数组。

下面的问题是,给定i,如何求2^k?

答案很简单:2^k=i&(i^(i-1)) ,也就是i&(-i)    为什么呢?? 请看下面:

整数运算 x&(-x),当x为0时结果为0;x为奇数时,结果为1;x为偶数时,结果为x中2的最大次方的因子。
       因为:x &(-x) 就是整数x与其相反数(负号取反)的按位与:1&1=1,0&1 =0, 0&0 =1。具体分析如下:
       □ 当x为0时,x&(-x) 即 0 & 0,结果为0;
       □ 当x不为0时,x和-x必有一个为正。不失一般性,设x为正。
       ●当x为奇数时,最后一个比特为1,取反加1没有进位,故x和-x除最后一位外前面的位正好相反,按位与结果为0。最后一位都为1,故结果为       1。
       ●当x为偶数,且为2的m次方(m>0)时,x的二进制表示中只有一位是1(从右往左的第m+1位),其右边有m位0,左边也都是0(个数由表示   x的字        节数决定),故x取反加1后,从右到左第有m个0,第m+1位及其左边全是1。这样,x& (-x) 得到的就是x。 
       ●当x为偶数,却不为2的m次方的形式时,可以写作x= y * (2^k)。其中,y的最低位为1。实际上就是把x用一个奇数左移k位来表示。这时,x的   二进制         表示最右边有k个0,从右往左第k+1位为1。当对x取反时,最右边的k位0变成1,第k+1位变为0;再加1,最右边的k位就又变成了0,第   k+1位因为进         位的关系变成了1。左边的位因为没有进位,正好和x原来对应的位上的值相反。二者按位与,得到:第k+1位上为1,左边右边都为   0。结果为2^k,即         x中包含的2的最大次方的因子。
        总结一下:x&(-x),当x为0时结果为0;x为奇数时,结果为1;x为偶数时,结果为x中2的最大次方的因子。 比如x=32,其中2的最大次方因子  为                 2^5,故x&(-x)结果为32;当x=28,其中2的最大次方因子为4,故x & (-x)结果为4。当x=24,其中2的最大次方因子为8,故 x&(-x)结果为  8。

(结合网址:http://blog.csdn.net/int64ago/article/details/7429868)

三、用途

(1)最基本的数组区间改变、更新、求和。

(2)逆序数

(3)二维线段树求矩阵的更新、求和等(暂未整理)


四、经典题型(详情:http://blog.csdn.net/zhengxu001/article/details/8029790)


源代码模板:

#include<stdio.h>
#include<string.h>
#include <iostream>
using namespace std;
#define MAX 100
int a[MAX],c[MAX];

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

void update(int x,int add){//更新树状数组   x为原数组的下标  add是原数组的值 
    while(x<MAX){//更新范围,如果数组没全部用到不用全部更新,条件可以设置 
        c[x]+=add;    
        x+=lowbit(x);
    }
}

int get_sum(int x){//求前n项和 
    int sum=0; 
    while(x!=0){       
        sum+=c[x];   
        x-=lowbit(x);
    }  
    return sum;
}

int main (){
	int a[]={0,1,2,3,4,5,6,7,8,9,10};
	for(int i=1;i<=10;i++)
	   update(i,a[i]);
	   
	for(int i=1;i<=100;i++)
	   cout<<c[i]<<' ';
	cout<<endl<<endl;
    for(int i=1;i<=100;i++)  
	   cout<<get_sum(i)<<' ';
	return 0;
}