zoukankan      html  css  js  c++  java
  • 树状数组

    树状数组

    树状数组具体的定义:查看百度百科

    树状数组的模拟理解可以是这样的,对于树状数组而言你完全可以把它看做是一个辅助的数组。至于它是什么样的辅助数组就和定义有关了。首先,你有一个数组A[100]里面存放着相应的数据,而这时又定义了一个辅助数组C[100],C[100]的作用就是记录A数组中某几个元素的和。而对于树状数组,我们规定C[1]存的是A[1],C[2]存的是A[1]+A[2]…如下图:

     

    既然我们已经知道C数组仅仅是起到辅助作用,那么我们在思考的时候完全不必想其构造过程,只需要懂得构造方法和C数组究竟代表什么即可。

    对于树状数组的算法一般都会有看到一个函数是:lowbit()函数,可以查看博客探究它的含义。现在只需要知道它是用来构造辅助数组C的就可以了。

    int lowbit(int x)

    {

        return x&(-x);

    }

    紧接着会是这样两个函数:

    int sum(int x)

    {

        int result=0;

        while(x>0)

        {

            result+=c[x];

            x-=lowbit(x);

        }

        return result;

    }

    void add(int x,int n)

    {

        while(x<MAX)

        {

            c[x]+=n;

            x+=lowbit(x);

        }

    }

    在这里你需要明确的就是这两个函数都和辅助数组C有关,但是实质是获取A的信息(因为C是辅助数组,用在存A中的信息的)。第一个函数是表示获取数组A[1]到A[x]的和。

    第二个函数则是在A数组中的第x位置上加上个数n。而我们在程序使用中并没有定义数组A(没必要浪费空间)原因是我们所有的操作都是在C(可以想象存在一个A,然后C它的辅助数组)上进行的,通过C来获取信息。

    这样基本上就算对树状数组有所了解了。有些人肯定会很疑问,不明白如果没有定义A如何构造C呢?其实这就归结于第二个函数add()  我们可以利用add()去构造C.

    例如:

    有一串数据  1  2  3  4

    我们想把他们存到A中(A[1]=1,A[2]=2,A[3]=3,A[4]=4)这里面没有用A[0]是因为lowbit()函数,所以树状数组使用应注意不能用0号空间。

    这时我们只需要

    for(i=1;i<=n;i++)

    {

       int temp=0;

       cin>>temp;

       add(i,temp);

    }

    可以理解为每次输入一个值,我就把它存到数组A的对应空间中,而数组A并不是真实存在的。但是数组C存在且add是针对数组C进行操作的。这样就利用了add构造了虚拟的数组A同时也构造了相应的辅助数组C。

    而我们想获得A数组前N项和的时候只需要sum(N)即可。(一定要理解好A和C的关系)

    树状数组使用变型1:

    上面已经介绍了关于构造数组C的函数,接下来就对同一个函数进行用法上的改变。

    对于add函数来说:

    for(i=1;i<=n;i++)

    {

       int temp=0;

       cin>>temp;

       add(temp,1);

    }

    这样的写法会令初学者困扰,而仔细看就很容易发现。其实就是输入一个数我们就以这个数为索引即:A[temp],让它的值为1。说白了就是把A数组用来计数,输入1我就在1号空间把其值变成1,输入1000就在1000号空间变1。而C数组的本质还没有改变,仍然指的是A某几个单元的和。仍然是辅助数组。

    树状数组使用变型2:

    第二种变型是涉及到两个函数add和sum的,这时你就需要去了解下add和sum函数的内部结构,和C数组的构造过程了,而不能单单关注他们的含义了。含义只是使其思考及其使用上更加方便,但实质还需要了解的。

    下面给出两个函数的变型:

    int sum(int index)

    {

             int result=0;

             while(index<MAX)//注意此处不同

             {

                     result+=c[index];

                     index+=lowbit(index); //注意此处不同

             }

             return result;

    }

    void add(int index, int number)

    {

             while(index>0) //注意此处不同

             {

                     c[index]+=number;

                     index-=lowbit(index); //注意此处不同

             }

    }

    如果两个函数进行了如上的更改,则其含义已然改变。此时add指的是将数组A的前N项都加上number。即:add(n,number);

    如:add(3,1)就是指将A数组的前三个空间都加1;

    则此时的C也并不代表是A的某几项元素之和了!因为他的构造的函数add已经发生了改变,这时C存的就是对应A的值。而sum函数就是返回当前索引下A数组中的值(也就是C数组中的值)

    如:C[3]=A[3]=1;则sum(3)的值就是1

    离散化

     

    对于数组变型1而言,我们曾用数组进行计数,输入100则就令A[100]=1。然而当我们输入的数比较大例如1000000000,那我们就没必要开那么大的数组了。例如如下一串数:

    1000 999 998

    我们想求998之前比998大的数,也就是998的逆序数。那么我们没必要开1000的数组去存三个数,而是将其进行离散化为 3 2 1即可。这样一来他们之间的大小关系和前后顺序没有受到任何影响,同时只需要开3个空间大小的数组即可。

    思路很简单,具体代码如下:

    struct data

    {

       int index;//输入的顺序的索引 第几个输入的就是几

       int value;//输入的值

    }a[MAX];

    for(i=1;i<=n;i++)

    {

        scanf("%d",&a[i].value);//获得值

        a[i].index=i;//获得索引

    }

    sort(a+1,a+n+1,cmpv);//利用值进行排序,显然先后顺序乱了

    for(i=1;i<=n;i++)//利用索引将其离散化,并还原成原来的输入顺序

    {

        b[a[i].index]=i;

    }

    对应习题分别为HDU 2492 POJ 2352 HDU 1556 POJ 2299

  • 相关阅读:
    oracle 数据库服务名怎么查
    vmware vsphere 6.5
    vSphere虚拟化之ESXi的安装及部署
    ArcMap中无法添加ArcGIS Online底图的诊断方法
    ArcGIS中字段计算器(高级计算VBScript、Python)
    Bad habits : Putting NOLOCK everywhere
    Understanding the Impact of NOLOCK and WITH NOLOCK Table Hints in SQL Server
    with(nolock) or (nolock)
    What is “with (nolock)” in SQL Server?
    Changing SQL Server Collation After Installation
  • 原文地址:https://www.cnblogs.com/pfdm/p/SZSZ.html
Copyright © 2011-2022 走看看