树状数组

树状数组是一种数据结构,常用于高效计算数列的前缀和与区间和,支持动态单点值的修改。基本思想是每个整数可表示为2的幂次和,通过lowbit函数更新元素并实现O(logn)的时间复杂度。常见应用场景包括PUIQ模型、IUPQ模型和逆序模型,例如在POJ等竞赛题目中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

相关概念

树状数组的定义

树状数组,又称二叉搜索树,初衷是解决数据压缩里的累积频率(Cumulative Frequency)的计算问题,现多用于高效计算数列的前缀和,区间和。它可以以 O(log⁡n )的时间得到任意前缀和∑_(i=1)^j▒a[i] ,1≤j≤N,并同时支持在O(log⁡〖n)〗时间内支持动态单点值的修改,空间复杂度 O(n)。

树状数组的基本思想

每个整数都能表示为一些2的幂次方的和,比如19,其二进制表示为10011,所以它能表示为:19=2^4+2^1+2^0。类似的,累积频率可表示为其子集合之和,其结构如图 1所示,由于树状数组的实现要表示为2的幂次和,而2^0=1,所以数组下标从1开始。

图1 树状数组结构

树状数组的应用
  1. PUIQ模型(Point Update Interval Query 点更新,段求和)
    一个长度为n(n <= 500000)的元素序列,一开始都为0,现给出三种操作:
    add x v:给第x个元素的值加上v;(a[x] += v )
    sub x v:给第x个元素的值减去v;(a[x] -= v )
    um x y:询问第x到第y个元素的和;(print sum{ a[i] | x <= i <= y } )
    这是树状数组最基础的模型,前两个操作就是对应的单点更新,3的操作就对应了成端求和。具体得,前两个操作只要分别调用add(x, v)和add(x, -v), 而最后一个操作则是输出sum(y) - sum(x-1)的值。
  2. IUPQ模型(Interval Update Point Query 段更新,点求值)
    一个长度为n(n <= 500000)的元素序列,一开始都为0,现给出两种操作:
    add x y v :给第x个元素到第y个元素的值都加上v;(a[i] += v, 其中 x <= i <= y )
    get x:询问第x个元素的值;(print a[x] )
    这类问题对树状数组稍微进行了一个转化,但是还是可以用add和sum这两个函数来解决,对于第一个操作我们只需要执行两个操作,即add(x, v)和add(y+1, -v);而第二个操作则是输出sum(x)的值。这样就把区间更新转化成了单点更新,单点求值转化成了区间求和。
  3. 逆序模型
    给定一个长度为n(n <= 500000)的排列a[i],求它的逆序对对数。对这个排列从后往前枚举,每次枚举到 Xi 元素时,执行cnt += sum(Xi-1),然后再执行add(Xi, 1),n个元素枚举完毕,得到的cnt值就是我们要求的逆序数了。总的时间复杂度O(nlogn)。这个模型和之前的区别在于它不是将原数组的下标作为树状数组的下标,而是将元素本身作为树状数组的下标。

相关算法

  • lowbit(i)函数实现
    该函数主要确定对应位的树状数组表示为从该位往前多少个元素的和,由图 2 1以及树状数组的性质可知,该位表示的是最低位1的位置表示的数量的元素的和,如BIT[6]表示的是5和6两个元素的和。所以可以直接使用位运算来实现,即i&(-i),该函数复杂度位O(1)。
  • update(index,elem)函数实现
    该函数主要更新树状数组中的元素,此时更新一个元素需要更新所有记录了包含该元素的和,由图 2 1和树状数组性质可知,对于第i位元素,其后lowbit(i)-1位由于低位总有1,所以一定不会包括第i位元素,所以下一个包括第i位元素的应该是BIT[lowbit(i)+i],依次向后更新。伪代码如下:
update(index,elem)
i = index
while i < n
BIT[i] += elem
i += lowbit(i)
由于每次都增加lowbit(i),所以至多log⁡n次便可以修改结束,即修改复杂度位O(log⁡〖n)〗。
    sum(index)函数实现
该函数实现对前index个元素求和,前面已经说过BIT[index]记录的是从index向前lowbit(index)个元素的和,所以下次累加的元素和应该是BIT[index – lowbit(index)],伪代码如下:
sum(index)
i = index
s = 0
while i > 0
s += BIT[i]
i -= lowbit(i)

由于每次减去lowbit(i)个元素,所以摊还复杂度位O(log⁡〖n)〗。
+ 初始化
树状数组的初始化较为简单,首先将BIT的所有元素全部置为0,这就相当于构造了一个所有元素都为0的树状数组,然后每次增加一个元素就相当于改变一个元素的值,直接调用update函数即可。
+ 优化
上述为了方便叙述,将lowbit单独实现成为了一个函数,在伪代码中不难看到大量调用了lowbit函数,但是其函数体十分简单,函数调用在运行过程中带来了大量的开销,所以可以将上述所有的lowbit函数替换为i&(-i),减少函数调用的开销。

应用举例

POJ3321 Apple Tree
POJ1990 MooFest
POJ 2892 Tunnel Warfare
poj2481 Cows

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值