树状数组简单介绍

  • Post author:
  • Post category:其他




什么是



l

o

w

b

i

t

lowbit






l


o


w


b


i


t






学习树状数组之前我们首先要知道什么是



l

o

w

b

i

t

lowbit






l


o


w


b


i


t





,



l

o

w

b

i

t

(

x

)

lowbit(x)






l


o


w


b


i


t


(


x


)





表示



x

x






x









2

2






2





进制下最低位的



1

1






1





以及它后面的



0

0






0





所表示的二进制数。例如



l

o

w

b

i

t

(

3

)

=

1

,

l

o

w

b

i

t

(

2

)

=

2

lowbit(3)=1,lowbit(2)=2






l


o


w


b


i


t


(


3


)




=








1


,




l


o


w


b


i


t


(


2


)




=








2







怎么迅速地求出



l

o

w

b

i

t

lowbit






l


o


w


b


i


t





呢?这里有一条简单的公式:



l

o

w

b

i

t

(

x

)

=

x

&

x

lowbit(x)=x \& -x






l


o


w


b


i


t


(


x


)




=








x


&













x






树状数组的定义

树状数组中的每一个节点都表示它相应区间的一个值(区间和,区间最大值等,可以自己决定,以下都用区间和作为例子),比如



C

x

C_x







C










x





















表示从



x

x






x





这个节点开始向前



l

o

w

b

i

t

(

x

)

lowbit(x)






l


o


w


b


i


t


(


x


)





个节点中这个区间的和如下图所示:

请添加图片描述

该图表示了树状数组与原数组之间的关系,我们可以将



1

,

4

,

7

,

3

,

2

,

8

,

6

,

10

1,4,7,3,2,8,6,10






1


,




4


,




7


,




3


,




2


,




8


,




6


,




1


0





代入原序列
请添加图片描述

这样就很清楚了吧,树状数组的表示方法一目了然。


树状数组的性质:




  1. C

    x

    C_x







    C










    x





















    表示以它为根的子树的所有子树的和比如



    C

    2

    =

    C

    1

    +

    A

    2

    C_2=C_1+A_2







    C










    2




















    =









    C










    1




















    +









    A










    2





















    (不重复计算



    A

    1

    A_1







    A










    1





















    )




  2. C

    x

    C_x







    C










    x





















    的父节点是



    C

    x

    +

    l

    o

    w

    b

    i

    t

    (

    x

    )

    C_{x+lowbit(x)}







    C











    x


    +


    l


    o


    w


    b


    i


    t


    (


    x


    )
























  3. C

    x

    C_x







    C










    x





















    的总叶节点个数(也就是表示的区间长度)等于



    l

    o

    w

    b

    i

    t

    (

    x

    )

    lowbit(x)






    l


    o


    w


    b


    i


    t


    (


    x


    )






单点修改区间查询



单点修改

单点修改时,只会影响到它到根节点的这条路径上的所有节点,再根据树状数组的性质



2

2






2





我们可以得到单点修改函数:

//给第x个节点加上y 
inline void xg(ll x,ll y){
	for(;x<=n;x+=lb(x))//枚举从当前节点到根节点
	c[x]+=y;//逐一修改
}



区间查询



求前缀和

树状数组还可以用来求前缀和,把每个不相交区间内的



C

C






C





都相加就可以得到前缀和了

//求前x个数的和
inline ll qz(int x){
	ll ans=0;for(;x;x-=lb(x))ans+=c[x];
	return ans;
}



区间求和

将两个端点的前缀和相减就可以得到区间和了

inline ll qz(int x){
	ll ans=0;for(;x;x-=lb(x))ans+=c[x];
	return ans;
}
inline ll sch(int l,int r){
	return (qz(r)-qz(l-1));
}



区间修改单点查询



分析

由于树状数组不是线段树,所以不能像上面那样维护区间和了,我们要维护差分值,让



C

x

=

A

x

A

x

1

(

A

0

=

0

)

C_x=A_x-A_{x-1}(A_0=0)







C










x




















=









A










x






























A











x





1



















(



A










0




















=








0


)






这样,区间修改时,只要修改区间两端的差分值就能做到修改整个区间。

inline void xg(ll x,ll y){
	for(;x<=n;x+=lb(x))
	c[x]+=y;
}
inline void add(ll l,ll r,ll x){
	xg(l,x);xg(r+1,-x);//左端点差值增加
}

那么怎么单点查询呢?

很明显,当用一个差分序列表示另一个序列时,



A

x

=

i

=

1

i

=

x

A_x=\sum_{i=1}^{i=x}







A










x




















=





















i


=


1










i


=


x























用树状数组求前缀和就好了

inline ll sch(ll x){
	ll ans=0;for(;x;x-=lb(x))ans+=c[x];
	return ans;
}



版权声明:本文为weixin_54799322原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。