POJ 2352(顺路讲解一下树状数组)

来源:互联网 发布:怎么测试网络是否掉包 编辑:程序博客网 时间:2024/04/25 07:23

接触到的第一道树状数组的题,AC之后感觉对树状数组思想的理解明显清晰了很多,入门必备呀。

先来讲讲树状数组吧。

上个图


如果要求区间和,例如求a3->a8,我们先用数组c来记录下来各个区间的和,那么就可以直接c8-c2就可以得到a3->a8的区间和了。

c[n]数组的下标是用来记录数组a[1]->a[n]的区间和。

再想,我们如果要求s->t的和可以怎么求?

是不是可以用(1->t)减去(1->s-1)?

同理,树状数组也可以用这种方式求和。比如求a[2]的话就可以用c[2]-c[1]来得到a[2]。

关于c数组的记录方法,就不得不提到x&(-x)这个表达式。

x&(-x)是用来计算每次查询或者更新时候c数组的偏移量。

通常写成函数的形式。

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


注意:计算机中负数是用的补码表示的,正数是用的原码表示,可以自己拿张草稿纸来实现一下x&(-x)的计算


关于树状数组的求区间和,如果求区间[1,8],那么直接c[8]就好,但是如果要求区间[4,8]该怎么办?

那么可以用区间[1,8]的和减去区间[1,3]的和就得到了区间[4,8]的和。

那又如何求区间[1,3]呢?我们可以c[2]+c[3]就得到了区间[1,3]的和了。

下面是求区间[1,x]和的函数:

int sum(int x){    int s = 0;    while (x > 0)    {        s += c[x];        x -= lowbit(x);    }    return s;}

以求区间[1,3]为例,首先x等于3代入方程,3 > 0,则s = c[3],然后执行x-=lowbit(x);

让我们进入lowbit函数,首先3的二进制原码为0000 0011,-3为3的补码,则-3的二进制码为1111 1101,进行&运算之后为1,所以x-=1,此时x为2。由于2>0,所以s此时等于c[3] + c[2]。执行x-=lowbit(x)之后x = 0。循环结束,此时s已经是区间[1,3]的和了。

我们再来看树状数组的更新函数:

int update(int x, int num){    while (x <= MAX)    {        c[x] += num;        x += lowbit(x);    }}

和线段树一样,树状数组也是需要对节点所影响到的所有节点进行更新,采取从根到顶的方式。也就是对每个影响到的节点都加上更改信息。

树状数组时间复杂度O(log n)

————————————————————————分割线——————————————————————————————

题意:有n个星星节点,存在星星节点左下角(包括正左和正下)的其他星星节点,则该星星节点比它左下角的星星节点大,level 0表示该星星节点没有比他还小的节点,level 1表示存在一个比该星星节点小的点。输出统计好的每个level等级存在多少星星节点。




解题思路:为什么要用树状数组呢,因为如果你用for循环统计的话,由于数据很大然后又有很多组数据需要统计,那么肯定是会超时的,所以此时需要一种高效的数据结构(感觉像一句废话TAT),树状数组类似于线段树,能够很高效的解决区间问题,将这道题提炼一下其实也就是一个统计区间和的问题,线段树写起来好麻烦的=。=于是乎用了树状数组。

直接上代码:

/*因为是按照y升序输入,所以后面输入对前面输入并无影响**前x与后x如果相同,那么肯定后x是包含前x的,因为是按照y升序**即前后x虽是在同一列,但后x肯定在前x上面,即包含前x**PS:树状数组下标从1开始*/#include <iostream>#include <cstdio>#include <cstring>using namespace std;int level[32001];int c[32001];int lowbit(int x){    return x & (-x);}int sum(int x){    int s = 0;    while (x > 0)    {        s += c[x];        x -= lowbit(x);    }    return s;}int update(int x){    while (x <= 32001)    {        c[x]++;        x += lowbit(x);    }}int main(){    int n;    int x, y;    while(~scanf("%d", &n))    {        int N = n;        memset(level, 0, sizeof(level));        memset(c, 0, sizeof(c));        while (n--)        {            scanf("%d %d", &x, &y);            level[sum(x+1)]++;            update(x+1);        }        for (int i = 0; i <= N - 1; i++)            printf("%d\n", level[i]);    }    return 0;}


0 0
原创粉丝点击