码迷,mamicode.com
首页 > 编程语言 > 详细

一维 + 二维树状数组 + 单点更新 + 区间更新 详解

时间:2015-08-08 01:24:09      阅读:1180      评论:0      收藏:0      [点我收藏+]

标签:

树状数组详解:
假设一维数组为A[i](i=1,2,...n),则与它对应的树状数组C[i](i=1,2,...n)是这样定义的: 
C1 = A1 
C2 = A1 + A2 
C3 = A3 
C4 = A1 + A2 + A3 + A4 
C5 = A5 
C6 = A5 + A6
.................
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 

................

技术分享

如图可知:
为奇数的时候他是代表他本身,而为偶数的时候则是代表着自己以及属于它管辖区域的和。
(1)C[x] 展开以后有多少项?由下面公式计算: 



int lowbit(int x) { //计算 C[x] 展开的项数
    return x & (-x);
}


得到了项数后x - lowbit(x),得到的即使前一区域的x
举个例子:
如果x = 6, x - lowbit(x) == 4 即为C[4]如此,此处说明x & (-x)的作用是求x转换为二进制时最后一个1的位置在哪里,至于为什么,这就是树状数组的神奇之处,一个非常牛逼的规律
然后更新单点的模板代码就成型:

void add(int pos, int c){
    while(pos <= n){
        C[pos] += c;
        pos += lowbit(pos);//转移到他的父亲节点,如果这个点更新C[4],那么下一个更新的点就是C[8],相当于他的父亲
    }
}


求和代码也已经成型:
int sum(int pos){
    int ret = 0;
    while(pos > 0){
        ret += C[pos];
        pos -= lowbit(pos);//当你要计算1..6的和时,结果即为C[4] + C[6]
    }
}


如果是二维的树状数组的话,心里思考一下,是不是感觉很眼熟哦!
其实他们的原理是一样的:
设二维数组为: 

  a[][]={{a11,a12,a13,a14,a15,a16,a17,a18}, 
             {a21,a22,a23,a24,a25,a26,a27,a28}, 
             {a31,a32,a33,a34,a35,a36,a37,a38}, 
             {a41,a42,a43,a44,a45,a46,a47,a48}};

那么C[1][1] = a11,C[1][2] = a11 + a12;
如此当C[1][i]...C[1][j]时跟一维的树状数组是没有什么区别的
那么C[2][1] = a11 + a21,C[2][2] = a11 + a12 + a21 + a21,如此可以发现
其实C[2][i].....C[2][j],就是C[1][],C[2][],单独的两个一维树状数组同一位置的值合并在一起
C[3][1] = a31,C[3][2] = a31 + a32......
C[4][1] = a11 + a21 + a31 + a41,C[4][2] = a11 + a12 + a21 + a22 + a31 + a32 + a41 + a42
有没有发现,如果单独把二维中的第一个维度拿出来A[1][m] + A[2][m] = C[2][m],A[3][m] = C[3][m],
是不是也和一维的数组一样,所以二维数组的规律就是,不管是横坐标还是纵坐标,将他们单独拿出来,他们
都符合x += lowbit(x),属于它的父亲节点.
如此二维数组的单点更新代码如下:

void add(int x, int y, int c){
//如果我改变了C[x][y]这个点,那么接下来C[x][y += lowbit(y)]当做一维数组的话都是要改变一个c的
//接着我们的纵坐标也是要改变的C[x += lowbit(x)][y]也是要改变的,应为他们都包含了C[x][y]这个集合
    for(int i = x;i < n;i += lowbit(i)){
        for(int j = y; j < n;j += lowbit(j)){
            C[i][j] += c;
        }
    }
}


那么求和代码就更加好办了
int sum(int x,int y){
    int ret = 0;
    for(int i = x; i > 0; i -= lowbit(i)){
        for(int j = y;j > 0;j -= lowbit(i)){
            ret += C[i][j];
        }
    }
    return ret;
}


这就是二维树状数组的成型,那么三维以及以上呢,想来大家都已经秒懂了,是的,没错,就是在最外一层加个循环跟一维变为二维原理一样

而对于区间增加以及减少依旧很简答

比如举个例子,我要将[x, y]区间增加d,那么可以先将1....y加上d,然后1.....x减去d

对于二维树状数组的区间更新也是如此

版权声明:本文为博主原创文章,未经博主允许不得转载。

一维 + 二维树状数组 + 单点更新 + 区间更新 详解

标签:

原文地址:http://blog.csdn.net/qq_18661257/article/details/47347995

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!