树状数组学习总结

这几天学习了树状数组。记录一些心得体会。

对于一个普通数组a[n],查询某一个元素的时间复杂度是O(1),对数组的某一部分求和的时间复杂度是O(n)。当题目要求大量计算区间和时,用普通方法很有可能超时。而树状数组作为一种数据结构,英文名binary index tree(二进制索引树)。从它的英文名字就可以有一个大体的了解,这种数据结构和“2”有关。其查询和求和的时间复杂度都是O(logn)。树状数组是一种折中的方案,当遇到大量计算区间和的题目直接套模板,但对于大量查询某一个元素值却要使用一定的技巧来转化(如下文提到的改段求点)。

再次强调的是,树状数组是一种数据结构,而不是一种算法,是一种工具而不提供解题思路。尤其是在做专题训练时,不要为了用树状数组而去找思路,而应该是先寻找思路,找到思路后,看题目要求,是否要求大量计算区间和,若是,则考虑是否可以用树状数组。

首先是最基本的概念,提供一个不错的博文:点击打开链接

树状数组分一维、二维,甚至可以是三维(三维也可以看做枚举二维的树状数组)。

树状数组的题目类型主要有三种,改点求段,改段求点,改段求段。这里提供一个不错的博客,详细讨论了这三种模型,在此借花献佛 点击打开链接

树状数组一些注意的地方:update函数和sum函数可以有两种方式,update若是向下更新,sum就是向上求和。update若是向上更新,sum就要向下求和。不同的题目选择不同的组合方式,会使问题简化。其次,树状数组下标从1开始,有些题目下标从0开始,要注意转化一下,否侧会导致程序死循环,TLE。最后,要注意一下二维或者三维求区间段和的方法,方法类似为求矩形的面积。

一些题目

hdu1166 敌兵布阵点击打开链接 模板题

AC代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<stdlib.h>
#include<set>
#include<map>
#include<queue>
using namespace std;

const int Max=50005;

int c[Max],n;

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

void  update(int i,  int x)
{
    while(i<=n)
    {
        c[i] = c[i]+x;
        i += lowbit(i);
    }
}

int sum(int i)
{
    int  ans = 0;
    while(i>0)
    {
        ans += c[i];
        //cout<<i<<" "<<c[i]<<' ';
        i -= lowbit(i);
    }

    return ans;
}



int main()
{
    int t;
    scanf("%d",&t);
    int count;
    for(count=1;count<=t;count++)
    {
        memset(c,0,sizeof(c));
        int i,j;
        scanf("%d",&n);
        for(i=1; i<=n; i++)
        {
            int x;
            scanf("%d",&x);
            update(i,x);

        }

        char str[7];
        printf("Case %d:\n",count);
        while(scanf("%s",&str)&&str[0]!='E')
        {
            scanf("%d%d",&i,&j);
            if(str[0]=='Q')
            {
                printf("%d\n",sum(j)-sum(i-1));
            }
            else if(str[0]=='A')
            {
                update(i,j);

            }
            else if(str[0]=='S')
            {
                update(i,-j);
            }

        }
    }

    return 0;
}				
hdu 1556 color the ball  点击打开链接 典型的改段求点

AC代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<cmath>

using namespace std;
const int Max=100005;

int c[Max],n;

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

void  update(int i,  int x)
{
    while(i<=n)
    {
        c[i] = c[i]+x;
        i += lowbit(i);
    }
}

int sum(int i)
{
    int  ans = 0;
    while(i>0)
    {
        ans += c[i];
        i -= lowbit(i);
    }

    return ans;
}

int main()
{
    int i,j,a,b;
    while(scanf("%d",&n) && n)
    {
        memset(c,0,sizeof(c));
        for(i=0; i<n; i++)
        {
            scanf("%d%d",&a,&b);
            update(a,1);
            update(b+1,-1);
        }
        printf("%d",sum(1));
        for(i=2; i<=n; i++)
        {
            printf(" %d",sum(i));

        }
        printf("\n");
    }
    return 0;
}
POJ 3067 Japan 求逆序数 解题报告点击打开链接

POJ2299 Ultra-QuickSort 逆序数+离散化 题目链接点击打开链接 解题报告:点击打开链接

POJ 1195 Mobile phones二维树状数组应用 注意求区间和公式的推导 解题报告点击打开链接

UFOs 三维树状数组,有了上一题求区间和的思想,公式就不难推了。解题报告点击打开链接

以上是对这几天学习树状数组的一点总结。树状数组就是帮助我们快速求得区间和的一种工具,但其中的一些处理技巧却值得细细品味。我在看很多博文时一直看不懂,感觉晦涩难懂,但是当我把操作过程模拟一遍之后,大脑就清晰很多了。绝知此事要躬行啊!



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值