zoukankan      html  css  js  c++  java
  • 数据结构--树状数组

    证明参考:https://zhuanlan.zhihu.com/p/297885717(抄的)

    树状数组支持的操作:单点修改,区间查询(仅限于支持减法的操作,不支持的例子:max)

    树状数组定义:定义C[ i ] 表示的是A [ i - lowbit(i) ] + 1  ~~   A[ i ] 之间的数据。

    C[ i ]的父节点为C[ i + lowbot ( i ) ]

    C[ i ]的子节点有C[ i - lowbit(i) ] , C[i - lowbit(i) -lowbit ( i - lowbit ( i ) ) ].........

    术语定义:对于一个点 i ,其覆盖的点的左端点记为L(i) , L(i) = i - lowbit( i )

         对于任意正整数x,x=a*2^(i+1) + 2^i,其中a>=0,i>=0,则lowbit(x)=2^i

    要证:(1)x+lowbit(x)能够覆盖x

       (2)x~x+lowbit(x)之间不存在能够覆盖x的数

        那么 i 的父节点是 i+lowbit(i) 得证。   

       (3)1<=x<=y<=z,y覆盖x,z覆盖y,那么z覆盖x

       (4)对于一个数x,覆盖的数有且仅有x , x-lowbit(x) , x-lowbit(x) - lowbit( x-lowbit(x) ).....

        那么 i 的子节点得证。

        【分析】因为具有传递覆盖的性质,且x覆盖的区间的并集是1~x,故得证。

    证明:(1)设x=a*2^(i+1)+2^i,则y=x+lowbit(x) = a*2^(i+1)+2^i+2^i = (a+1)*2^(i+1)

         所以 lowbit(y) >= 2^(i+1) 

         x-L(y) = x  - (x+lowbit(x) - lowbit(y) +1)

            =lowbit(y) - lowbit(x) - 1

            >=2^(i+1)-2^i - 1 

            =2^i - 1 >= 0

        故L(y)>=x,即y覆盖x,C[ i+lowbit(i) ]确实可以完全覆盖 C[ i ]

        (2)设x<y,要证明对于任意的x<z<y,z不覆盖x

          y=x+lowbit(x)=x + 2^i

          由于x < z < y,因此z=x+b,其中b的范围是[1, 2^i-1]

          显然lowbit(z) = lowbit(b)

          又由于b >= lowbit(b)

          因此L(z) = z-lowbit(z)+1=x+b-lowbit(b)+1>=x+1>x

        (3)设1<=x<=y<=z,若y覆盖x,z覆盖y,那么z覆盖x

          可证[L(y),y]属于[L(z),z],那么因为x属于[L(y),y],z覆盖x得证。

          z可以表示为a*2^(i+1)+2^i, 其中lowbit(z) = 2^i,L(z) = a*2^(i+1)+1

          因为z覆盖y,则y>=L(z)=a*2^(i+1)+1, 并且y<=z = a*2^(i+1)+2^i,

          所以y可以表示为y=a*2^(i+1)+b ,其中b的取值范围是[1,2^i];

          所以lowbit(y) = lowbit(b)

          并且显然有b-lowbit(b)>= 0

          因此可得L(y)=y-lowbit(y)+1=a*2^(i+1)+b-lowbit(b)+1

          >=a*2^(i+1)+1=L(z)

          即L(y)>=L(z)

          又由于y<=z

          因此区间[L(y]),y] 属于区间[L(z),z]

          又由于x属于[L(y),y],因此x也属于区间[L(z),z],即z覆盖x;

          证明完毕。

        (4)对于任意正整数x,覆盖x的所有整数有且仅有x,x+lowbit(x),x+lowbit(x)+lowbit(x+lowbit(x)),...

          这一序列用p来表示,即p(0),p(1),p(2),p(3),...

          其中p(0) = x,当i>0时,p(i) = p(i-1)+lowbit(p(i-1))

          命题可以拆开为2部分,第一部分是p序列中任意数都覆盖x,第二部分是任意不在p序列的数字都不覆盖x,接下来分别对两部分进行证明。

          第一部分,p序列中任意数都覆盖x;

          使用数学归纳法证明,

          当i=0时,p(0)=x,显然p(0)覆盖x;

          假设i=k时,p(k)覆盖x;

          当i=k+1时,由p序列定义可知p(k+1)=p(k)+lowbit(p(k)),由反向覆盖性质可知,p(k+1)覆盖p(k),另外p(k)覆盖x,由覆盖的传递性质可知,p(k+1)也覆盖x;

          第二部分是,任意不在p序列的整数都不覆盖x;

          也就是区间序列[1,p(0)),(p(0),p(1)),(p(1),p(2)),(p(2),p(3)),…… 中任意正整数都不覆盖x,因此只需要证明任意一个区间中的任意正整数都不覆盖x即可;

          对于区间[1,p(0))的任意整数y,y<p(0),即y<x,显然y不覆盖x;

          设i是序列p中任意下标,接下来证明任意正整数y都不覆盖x,其中y属于区间(p(i),p(i+1));

          由反向覆盖的排他性可知,y不覆盖p(i),并且L(y)>p(i),又由于p(i)>=x,因此L(y)>x,即y也不覆盖x;

          证明完毕。


    下面简述单点修改和区间查询的具体操作。

      

    查询1~x的和:

    1 int query(int x){
    2     int sum=0;
    3     for(int i=x;i;i-=lowbit(i)){
    4         sum+=tr[i];
    5     }
    6     return sum;
    7 }

    修改x点的值:

    1 void add(int x,int y){
    2     for(int i=x;i<=n;i+=lowbit(i)){
    3         tr[i]+=y;
    4     }
    5 }

    lowbit函数:

      假设x=a*2^(i+1)+2^i,lowbit(i)=2^i,根据计算机内整数是以补码存储的特性

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

     例题:https://www.acwing.com/problem/content/1266/

    单点查询,区间修改。

     1 #include <iostream>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 const int N=100010;
     6 int a[N],tr[N];
     7 int n,m;
     8 int lowbit(int x){
     9     return x&-x;
    10 }
    11 void add(int x,int y){
    12     for(int i=x;i<=n;i+=lowbit(i)){
    13         tr[i]+=y;
    14     }
    15 }
    16 int query(int x){
    17     int sum=0;
    18     for(int i=x;i;i-=lowbit(i)){
    19         sum+=tr[i];
    20     }
    21     return sum;
    22 }
    23 int main()
    24 {
    25 
    26     cin>>n>>m;
    27     for(int i=1;i<=n;i++){
    28         cin>>a[i];
    29         add(i,a[i]);
    30     }
    31     for(int i=0;i<m;i++){
    32         int k,x,y;
    33         cin>>k>>x>>y;
    34         if(k==0) cout<<query(y)-query(x-1)<<endl;
    35         else if(k==1) add(x,y);
    36     }
    37     return 0;
    38 }

    若是每次操作是将一个数变为另一个数,则还需要维护a数组,这样的话就方便计算出差值。

    另:树状数组本质上是只能完成单点修改区间查询的操作,但是结合一些方法还是能够得到较好的应用的。

      比如结合差分便可实现区间修改,单点查询。

  • 相关阅读:
    centos7安装Nginx
    activiti数据库表结构剖析
    Consider defining a bean named 'entityManagerFactory' in your configuration解决办法
    mongodb远程数据库的连接以及备份导入导出数据
    在阿里云ECS CentOS7上部署基于MongoDB+Node.js的博客
    node.js搭建Web服务器
    Spring Boot 引入org.springframework.boot.SpringApplication出错
    CentOS7搭建SVN服务器
    Linux环境下MySQL报Table 'xxx' doesn't exist错误解决方法
    Linux内核协议栈 NAT性能优化之FAST NAT
  • 原文地址:https://www.cnblogs.com/greenofyu/p/14820778.html
Copyright © 2011-2022 走看看