• [算法 树状数组]


    要学树状数组的先看懂一幅图

    这就是树状数组的存储方式。

    那么树状数组的优点是什么呢,允许任意修改,可快速提取出a数组内数字

    据图可知

    c1=a1,

    c2=a1+a2,

    c3=a3,

    c4=a1+a2+a3+a4,

    c5=a5,

    c6=a5+a6,

    c7=a7,

    c8=a1+a2+a3+a4+a5+a6+a7+a8,

    c9=a9,

    c10=a9+a10,

    c11=a11.......

    .c16=a1+a2+a3+a4+a5+.......+a16。

    分析上面的几组式子可知,当 i 为奇数时,ci=ai ;当 i 为偶数时,就要看 i 的因子中最多有二的多少次幂,例如,6 的因子中有 2 的一次幂,等于 2 ,所以 c6=a5+a6(由六向前数两个数的和),4 的因子中有 2 的两次幂,等于 4 ,所以 c4=a1+a2+a3+a4(由四向前数四个数的和)。

    (一)因此得到公式    cn=a(n-a^k+1)+.........+an(其中 k 为 n 的二进制表示中从右往左数的 0 的个数)。

    那么,如何求 a^k 呢?求法如下:

     1 int lowbit(int x)

    2 {

    3     return x&(-x);

    4 } 

    为啥那?

    以68为例,他的二进制是(68)2=1000100. 那么-68呢?因为计算机里的整数采用补码表示(补码是原码取反加一),因此-68实际上是68按位取反,末尾加一以后的结果。如下表(忽略符号位):

    原码   1 0 0 0 1 0 0

              ↓

    反码   0 1 1 1 0 1 1

             ↓

    补码   0 1 1 1 1 0 0

    我们把(68)2和(-68)2放在一起看一看:

    1 0 0 0 1 0 0

    0 1 1 1 1 0 0

    发现他们末尾带的0是一样多的。因此lowbit(x)=x&-x.   (&是转换成二进制之后进行与运算;而&&是直接进行与运算)

     

    (二)由此,我们就可以求出c数组了

     

    1 int sum(int x)
    2 {
    3     int sum=0;
    4     while(x>0)
    5     {
    6         sum+=c[x];
    7         x-=lowbit(x);
    8     }
    9 }

     

    (三)当数组中的元素有变更时,树状数组就发挥它的优势了,算法如下(修改为给某个节点 i 加上 x ):

    1 void change(int i,int x)
    2 {
    3     while(i<=n)
    4     {
    5         c[i]+=x;
    6         i+=lowbit(i)
    7     }
    8 }

    嗯。就这样。不算难。大概就是一种可变更的存储方式吧

     

  • 相关阅读:
    java拦截器与过滤器打印请求url与参数
    mybatis学习笔记(六)使用generator生成mybatis基础配置代码和目录结构
    【IDEA】IDEA创建Maven的Web项目并运行以及打包
    【环境变量】Linux 下三种方式设置环境变量与获取环境变量
    【Git】GitHub的SSH提交配置[
    spring配置redis注解缓存
    【查看linux配置】查看linux系统常用的命令,Linux查看系统配置常用命令
    Redis集群
    linux中wget 、apt-get、yum rpm区别
    spring+redis的集成,redis做缓存
  • 原文地址:https://www.cnblogs.com/taojy/p/7182686.html
Copyright © 2020-2023  润新知