zoukankan      html  css  js  c++  java
  • Evanyou Blog 彩带

      树状数组总结与讲解

      部分参考自:https://www.cnblogs.com/hsd-/p/6139376.html

            http://blog.csdn.net/yexiaohhjk/article/details/510775

      转载请注明出处

    树状数组

      首先大家都知道二叉树,如下图:

      

      那么现在变形一下:

      现在定义每一列的顶端为c数组:

      c数组代表子树的叶子结点权值和,现在计算来举个栗子:

      c[1]=a[1]

      c[2]=a[1]+a[2]

      c[3]=a[3]

      c[4]=a[1]+a[2]+a[3]+a[4]

      c[5]=a[5]

      c[6]=a[5]+a[6]

      c[7]=a[7]

      c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]

      那么再把c数组的下标转换为二进制数:

      1(0001) c[1]=a[1]

      2(0010) c[2]=a[1]+a[2]

      3(0011) c[3]=a[3]

      4(0100) c[4]=a[1]+a[2]+a[3]+a[4]

      5(0101) c[5]=a[5]

      6(0110) c[6]=a[5]+a[6]

      7(0111) c[7]=a[7]

      8(1000) c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]

      观察可以得出C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i]; (k为i的二进制中从最低位到高位连续零的长度)例如i=8时,k=3;

      那么这里引入lowbit(i),即取出i的二进制最低位1,也就是lowbit(i)=2^k,k的定义同上,代码也简洁明了:  

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

      这里x&(-x)就可以算出lowbit值,因为计算机中采用对应正数的补码来表示负数,例如:6(0110),那么-6(1010)即1001+1,所以6&(-6)=2,即lowbit(6)。

      C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i];
      C[i]=A[i-lowbit(i)+1]+A[i-lowbit(i)+2]+......A[i];  
     
      

    基本操作

     

      基本操作包括单点修改和区间查询
      1,单点修改
      将原数组的某一个元素a[i]加上或减去一个x时,那么需要维护c数组,这里只需要从i开始,然后令i+=lowbit(i),直到i=n为止,令所扫过的范围内的c数组全部修改即可。很容易证明,a[i]只包含在c[i],c[i+lowbit(i)],c[i+lowbit(i)+lowbit(i+lowbit(i))]...中,所以以上操作可行。
      举个栗子:
      当n=8,修改的i=3时
      因为
      c[3]=a[3]
      c[4]=a[1]+a[2]+a[3]+a[4]
      c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]
      因此需要修改c[3],c[4],c[8],又容易算出3+lowbit(3)=4,4+lowbit(4)=8
      所以以上算法可行
      Code:
    inline void add(int num,int x)
    {
        for(int i=num;i<=n;i+=lowbit(i))
        c[i]+=x;
    }

      初始化的时候也就直接用这个函数就行了  

      2,区间查询 

      求区间[x,y]内所有元素的和,这就要用到c数组了。

      首先看个栗子:

      求[3,7]区间内的和,已知:

      c[2]=a[1]+a[2]

      c[4]=a[1]+a[2]+a[3]+a[4]

      c[6]=a[5]+a[6]

      c[7]=a[7]

      所以c[4]+c[6]+c[7]-c[2]就是我们所要求的值,又可以知道

      7-lowbit(7)=6

      6-lowbit(6)=4

      4-lowbit(4)=0

      2-lowbit(2)=0

      因此这里我们定义一个函数quary(num),表示从num到1按照lowbit的递减方式所得到的所有c[i]的和,那么所要求的值就可以表示成:

      quary(7)-quary(3-1)

      因此,求区间[x,y]的和的方法就是quary(y)-quary(x-1)

      Code:

    inline int quary(int num)
    {
        int ret=0;
        for(int i=num;i>=1;i-=lowbit(i))
        ret+=c[i];
        return ret;
    }

      

      3,其他操作

      除了单点修改和区间查询,有时树状数组也会用来进行区间修改和单点查询。

      这时我们在初始化的时候需要将初始值减去上一个值,也就是add(i,a[i]-a[i-1]),根据树状数组的性质可以证明,查询时quary(x)实际上就是a[x]的值。

      那么将区间[x,y]内的每一个值加上或减去a就可以写成

      add(x,a);add(y+1,-a);

      正确性也可以用树状数组的性质证明。

      除此以外,树状数组还可以用于求逆序对等操作,这里就不一一详细介绍了。

    题目

      HDU 1166 敌兵布阵

      HDU 1556 color the ball

      POJ 2299 ultra-quicksort

     
  • 相关阅读:
    Build 2019 彩蛋
    崂山
    Win10 iot 修改日期时间
    《 结网:改变世界的互联网产品经理 》
    <[你在荒废时间的时候别人都在拼命!]>
    《时间的玫瑰》阅读笔记
    翻石头价值投资手册-科技行业
    No module named flask.ext.sqlalchemy.SQLALchemy
    《寻找伟大的企业》
    <《基金经理投资笔记丛书4-1:投资是一种生活方式》>
  • 原文地址:https://www.cnblogs.com/cytus/p/8587329.html
Copyright © 2011-2022 走看看