数据结构--树状数组

证明参考: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数组,这样的话就方便计算出差值。

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

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

 

posted on 2021-05-28 09:23  greenofyu  阅读(61)  评论(0编辑  收藏  举报