证明参考: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数组,这样的话就方便计算出差值。
另:树状数组本质上是只能完成单点修改区间查询的操作,但是结合一些方法还是能够得到较好的应用的。
比如结合差分便可实现区间修改,单点查询。