离线询问与树状数组

最近在做牛客网ACM暑假多校训练营的题目,其中J题就是用到了有关树状数组的知识。
于是先去blog上面看了一下什么是数组的询问,什么是区间和,点此打开链接

数组的询问举个例子就是:

给出M次询问,每次询问的格式为:L, R,表示求S = A[L] + A[L+1] + ... + A[R]的值。若S 的值为0,输出"YES",否则输出"NO"。M次询问,就是给出每个不完全相同的询问区间。

利用前缀来处理数组的查询求和将会很方便,但是如果加上一个条件:再q次询问中,如果将a[m]加上或减去一个k(update(m,k))操作,也可能会查询一个区间的和,怎么办呢?

如果还是使用前缀和数组,那就没有之前方便了,因为update(m,k)的缘故,所以我们需要更新s[m]到s[n]的值,这个时候算数复杂度又变回了O(q(n));

这个时候就需要树状数组来辅助解决这个问题了。

引用一个大神blog点此打开链接

1、树状数组的意义 
一个简单的前缀和数组虽然能在通常意义下做到最快的求和,对于数组的维护却相当费力。对输入数组的单点修改,简单前缀和数组的维护成本为O(n),那么对于需要频繁更改数据的维护,简单前缀数组是不合适的。 
有没有一种方法在修改和求和统计上能够同时做到O(1)呢?很遗憾并没有在所有情况下都这么快的结构。 
退而求其次,我们找到了一种在修改和求和统计速度都及其接近O(1)的结构,那就是树状数组。

2、树状数组的由来 
想像这样一种树,它是一种修改版的前缀和,这种微妙的修改使得Tree[i]是i之前多个数的和(但不是严格的从1到i的和),这样使得修改的时候没必要修改像简单前缀和一样的次数。但这似乎使得区间求和不太方便,再想象这种性质:Tree[i]是i前k个数的和而这个k是不确定的,它有时比较小,有时候非常大,这使得不论求小区间还是大区间都能很方便。 
那这个有时大有时小的数如何控制呢?这就是树状数组的核心:lowbit(x)=x&-x,这是一个与x相关的数,能够恰如其分地控制前缀范围。

3、lowbit(x)函数 
记n为x能够整除2的次数,那么这个函数的值就是2的n次方。 
不难想象,当x为2的k次方时,Tree[lowbit(x)]恰好为1~N所有数的和。当x为奇数时,Tree[lowbit(x)]仅仅是import[x],而x为普通偶数时,lowbit[x]时大时小。       

树状数组中的lowbit函数相当的关键。

比如在update操作中修改原数组中某一个元素A[i]的值,我们可以直接通过lowbit函数来遍历得到在树状数组中的所有与这个元素相关的新元素。下面给出代码 :

                           

                                                                                               

Sum操作中

我们通过求出当前的树状数组中的元素的lowbit(i)来返回上一级的树状数组元素,举个例子,数组Z是数组A的树状数组,Z[8]=A[1] + A[2] + A[3] + A[4] + A[5] + A[6] + A[7] + A[8],然后i-=lowbit(i),此时i=0,循环完毕,也就是球的Z[8]就是sum。

 

 

 

 

没有更多推荐了,返回首页