LogoCSP Wiki By Yundou
3 DS

树状数组

引入

树状数组是一种支持 单点修改区间查询 的,代码量小的数据结构。

什么是「单点修改」和「区间查询」?

假设有这样一道题:

已知一个数列 aa,你需要进行下面两种操作:

  • 给定 x,yx, y,将 a[x]a[x] 自增 yy
  • 给定 l,rl, r,求解 a[lr]a[l \ldots r] 的和。

其中第一种操作就是「单点修改」,第二种操作就是「区间查询」。

类似地,还有:「区间修改」、「单点查询」。它们分别的一个例子如下:

  • 区间修改:给定 l,r,xl, r, x,将 a[lr]a[l \ldots r] 中的每个数都分别自增 xx
  • 单点查询:给定 xx,求解 a[x]a[x] 的值。

注意到,区间问题一般严格强于单点问题,因为对单点的操作相当于对一个长度为 11 的区间操作。

普通树状数组维护的信息及运算要满足 结合律可差分,如加法(和)、乘法(积)、异或等。

  • 结合律:(xy)z=x(yz)(x \circ y) \circ z = x \circ (y \circ z),其中 \circ 是一个二元运算符。
  • 可差分:具有逆运算的运算,即已知 xyx \circ yxx 可以求出 yy

事实上,树状数组能解决的问题是线段树能解决的问题的子集:树状数组能做的,线段树一定能做;线段树能做的,树状数组不一定可以。然而,树状数组的代码要远比线段树短,时间效率常数也更小,因此仍有学习价值。

有时,在差分数组和辅助数组的帮助下,树状数组还可解决更强的 区间加单点值区间加区间和 问题。

树状数组

初步感受

先来举个例子:我们想知道 a[17]a[1 \ldots 7] 的前缀和,怎么做?

一种做法是:a1+a2+a3+a4+a5+a6+a7a_1 + a_2 + a_3 + a_4 + a_5 + a_6 + a_7,需要求 77 个数的和。

但是如果已知三个数 AABBCCA=a[14]A = a[1 \ldots 4] 的和,B=a[56]B = a[5 \ldots 6] 的总和,C=a[77]C = a[7 \ldots 7] 的总和(其实就是 a[7]a[7] 自己)。你会怎么算?你一定会回答:A+B+CA + B + C,只需要求 33 个数的和。

这就是树状数组能快速求解信息的原因:我们总能将一段前缀 [1,n][1, n] 拆成 不多于 logn\boldsymbol{\log n} 段区间,使得这 logn\log n 段区间的信息是 已知的

于是,我们只需合并这 logn\log n 段区间的信息,就可以得到答案。相比于原来直接合并 nn 个信息,效率有了很大的提高。

不难发现信息必须满足结合律,否则就不能像上面这样合并了。

下面这张图展示了树状数组的工作原理:

示意图
示意图

最下面的八个方块代表原始数据数组 aa。上面参差不齐的方块(与最上面的八个方块是同一个数组)代表数组 aa 的上级——cc 数组。

cc 数组就是用来储存原始数组 aa 某段区间的和的,也就是说,这些区间的信息是已知的,我们的目标就是把查询前缀拆成这些小区间。

例如,从图中可以看出:

  • c2c_2 管辖的是 a[12]a[1 \ldots 2]
  • c4c_4 管辖的是 a[14]a[1 \ldots 4]
  • c6c_6 管辖的是 a[56]a[5 \ldots 6]
  • c8c_8 管辖的是 a[18]a[1 \ldots 8]
  • 剩下的 c[x]c[x] 管辖的都是 a[x]a[x] 自己(可以看做 a[xx]a[x \ldots x] 的长度为 11 的小区间)。

不难发现,c[x]c[x] 管辖的一定是一段右边界是 xx 的区间总信息。我们先不关心左边界,先来感受一下树状数组是如何查询的。

举例:计算 a[17]a[1 \ldots 7] 的和。

过程:从 c7c_{7} 开始往前跳,发现 c7c_{7} 只管辖 a7a_{7} 这个元素;然后找 c6c_{6},发现 c6c_{6} 管辖的是 a[56]a[5 \ldots 6],然后跳到 c4c_{4},发现 c4c_{4} 管辖的是 a[14]a[1 \ldots 4] 这些元素,然后再试图跳到 c0c_0,但事实上 c0c_0 不存在,不跳了。

我们刚刚找到的 ccc7,c6,c4c_7, c_6, c_4,事实上这就是 a[17]a[1 \ldots 7] 拆分出的三个小区间,合并得到答案是 c7+c6+c4c_7 + c_6 + c_4

举例:计算 a[47]a[4 \ldots 7] 的和。

我们还是从 c7c_7 开始跳,跳到 c6c_6 再跳到 c4c_4。此时我们发现它管理了 a[14]a[1 \ldots 4] 的和,但是我们不想要 a[13]a[1 \ldots 3] 这一部分,怎么办呢?很简单,减去 a[13]a[1 \ldots 3] 的和就行了。

那不妨考虑最开始,就将查询 a[47]a[4 \ldots 7] 的和转化为查询 a[17]a[1 \ldots 7] 的和,以及查询 a[13]a[1 \ldots 3] 的和,最终将两个结果作差。

示意图
查询

管辖区间

那么问题来了,c[x](x1)c[x](x \ge 1) 管辖的区间到底往左延伸多少?也就是说,区间长度是多少?

树状数组中,规定 c[x]c[x] 管辖的区间长度为 2k2^{k},其中:

  • 设二进制最低位为第 00 位,则 kk 恰好为 xx 二进制表示中,最低位的 1 所在的二进制位数;
  • 2k2^kc[x]c[x] 的管辖区间长度)恰好为 xx 二进制表示中,最低位的 1 以及后面所有 0 组成的数。

举个例子,c88c_{88} 管辖的是哪个区间?

因为 88(10)=01011000(2)88_{(10)}=01011000_{(2)},其二进制最低位的 1 以及后面的 0 组成的二进制是 1000,即 88,所以 c88c_{88} 管辖 88aa 数组中的元素。

因此,c88c_{88} 代表 a[8188]a[81 \ldots 88] 的区间信息。

我们记 xx 二进制最低位 1 以及后面的 0 组成的数为 lowbit(x)\operatorname{lowbit}(x),那么 c[x]c[x] 管辖的区间就是 [xlowbit(x)+1,x][x-\operatorname{lowbit}(x)+1, x]

这里注意:lowbit\boldsymbol{\operatorname{lowbit}} 指的不是最低位 1 所在的位数 k\boldsymbol{k},而是这个 1 和后面所有 0 组成的 2k\boldsymbol{2^k}

怎么计算 lowbit?根据位运算知识,可以得到 lowbit(x) = x & -x

lowbit 的原理

x 的二进制所有位全部取反,再加 1,就可以得到 -x 的二进制编码。例如,66 的二进制编码是 110,全部取反后得到 001,加 1 得到 010

设原先 x 的二进制编码是 (...)10...00,全部取反后得到 [...]01...11,加 1 后得到 [...]10...00,也就是 -x 的二进制编码了。这里 x 二进制表示中第一个 1x 最低位的 1

(...)[...] 中省略号的每一位分别相反,所以 x & -x = (...)10...00 & [...]10...00 = 10...00,得到的结果就是 lowbit

int lowbit(int x) {
  // x 的二进制中,最低位的 1 以及后面所有 0 组成的数。
  // lowbit(0b01011000) == 0b00001000
  //          ~~~~^~~~
  // lowbit(0b01110010) == 0b00000010
  //          ~~~~~~^~
  return x & -x;
}

区间查询

接下来我们来看树状数组具体的操作实现,先来看区间查询。

回顾查询 a[47]a[4 \ldots 7] 的过程,我们是将它转化为两个子过程:查询 a[17]a[1 \ldots 7] 和查询 a[13]a[1 \ldots 3] 的和,最终作差。

其实任何一个区间查询都可以这么做:查询 a[lr]a[l \ldots r] 的和,就是 a[1r]a[1 \ldots r] 的和减去 a[1l1]a[1 \ldots l - 1] 的和,从而把区间问题转化为前缀问题,更方便处理。

事实上,将有关 lrl \ldots r 的区间询问转化为 1r1 \ldots r1l11 \ldots l - 1 的前缀询问再差分,在竞赛中是一个非常常用的技巧。

那前缀查询怎么做呢?回顾下查询 a[17]a[1 \ldots 7] 的过程:

c7c_{7} 往前跳,发现 c7c_{7} 只管辖 a7a_{7} 这个元素;然后找 c6c_{6},发现 c6c_{6} 管辖的是 a[56]a[5 \ldots 6],然后跳到 c4c_{4},发现 c4c_{4} 管辖的是 a[14]a[1 \ldots 4] 这些元素,然后再试图跳到 c0c_0,但事实上 c0c_0 不存在,不跳了。

我们刚刚找到的 ccc7,c6,c4c_7, c_6, c_4,事实上这就是 a[17]a[1 \ldots 7] 拆分出的三个小区间,合并一下,答案是 c7+c6+c4c_7 + c_6 + c_4

观察上面的过程,每次往前跳,一定是跳到现区间的左端点的左一位,作为新区间的右端点,这样才能将前缀不重不漏地拆分。比如现在 c6c_6 管的是 a[56]a[5 \ldots 6],下一次就跳到 51=45 - 1 = 4,即访问 c4c_4

我们可以写出查询 a[1x]a[1 \ldots x] 的过程:

  • c[x]c[x] 开始往前跳,有 c[x]c[x] 管辖 a[xlowbit(x)+1x]a[x-\operatorname{lowbit}(x)+1 \ldots x]
  • xxlowbit(x)x \gets x - \operatorname{lowbit}(x),如果 x=0x = 0 说明已经跳到尽头了,终止循环;否则回到第一步。
  • 将跳到的 cc 合并。

实现时,我们不一定要先把 cc 都跳出来然后一起合并,可以边跳边合并。

比如我们要维护的信息是和,直接令初始 ans=0\mathrm{ans} = 0,然后每跳到一个 c[x]c[x]ansans+c[x]\mathrm{ans} \gets \mathrm{ans} + c[x],最终 ans\mathrm{ans} 就是所有合并的结果。

int getsum(int x) {  // a[1]..a[x]的和
  int ans = 0;
  while (x > 0) {
    ans = ans + c[x];
    x = x - lowbit(x);
  }
  return ans;
}

树状数组与其树形态的性质

在讲解单点修改之前,先讲解树状数组的一些基本性质,以及其树形态来源,这有助于更好理解树状数组的单点修改。

我们约定:

  • l(x)=xlowbit(x)+1l(x) = x - \operatorname{lowbit}(x) + 1。即,l(x)l(x)c[x]c[x] 管辖范围的左端点。
  • 对于任意正整数 xx,总能将 xx 表示成 s×2k+1+2ks \times 2^{k + 1} + 2^k 的形式,其中 lowbit(x)=2k\operatorname{lowbit}(x) = 2^k
  • 下面「c[x]c[x]c[y]c[y] 不交」指 c[x]c[x] 的管辖范围和 c[y]c[y] 的管辖范围不相交,即 [l(x),x][l(x), x][l(y),y][l(y), y] 不相交。「c[x]c[x] 包含于 c[y]c[y]」等表述同理。

性质 1\boldsymbol{1}:对于 xy\boldsymbol{x \le y},要么有 c[x]\boldsymbol{c[x]}c[y]\boldsymbol{c[y]} 不交,要么有 c[x]\boldsymbol{c[x]} 包含于 c[y]\boldsymbol{c[y]}

证明

证明:假设 c[x]c[x]c[y]c[y] 相交,即 [l(x),x][l(x), x][l(y),y][l(y), y] 相交,则一定有 l(y)xyl(y) \le x \le y

yy 表示为 s×2k+1+2ks \times 2^{k +1} + 2^k,则 l(y)=s×2k+1+1l(y) = s \times 2^{k + 1} + 1。所以,xx 可以表示为 s×2k+1+bs \times 2^{k +1} + b,其中 1b2k1 \le b \le 2^k

不难发现 lowbit(x)=lowbit(b)\operatorname{lowbit}(x) = \operatorname{lowbit}(b)。又因为 blowbit(b)0b - \operatorname{lowbit}(b) \ge 0

所以 l(x)=xlowbit(x)+1=s×2k+1+blowbit(b)+1s×2k+1+1=l(y)l(x) = x - \operatorname{lowbit}(x) + 1 = s \times 2^{k +1} + b - \operatorname{lowbit}(b) +1 \ge s \times 2^{k +1} + 1 = l(y),即 l(y)l(x)xyl(y) \le l(x) \le x \le y

所以,如果 c[x]c[x]c[y]c[y] 相交,那么 c[x]c[x] 的管辖范围一定完全包含于 c[y]c[y]

性质 2\boldsymbol{2}c[x]\boldsymbol{c[x]} 真包含于 c[x+lowbit(x)]\boldsymbol{c[x + \operatorname{lowbit}(x)]}

证明

证明:设 y=x+lowbit(x)y = x + \operatorname{lowbit}(x)x=s×2k+1+2kx = s \times 2^{k + 1} + 2^k,则 y=(s+1)×2k+1y = (s + 1) \times 2^{k +1}l(x)=s×2k+1+1l(x) = s \times 2^{k + 1} + 1

不难发现 lowbit(y)2k+1\operatorname{lowbit}(y) \ge 2^{k + 1},所以 l(y)=(s+1)×2k+1lowbit(y)+1s×2k+1+1=l(x)l(y) = (s + 1) \times 2^{k + 1} - \operatorname{lowbit}(y) + 1 \le s \times 2^{k +1} + 1= l(x),即 l(y)l(x)x<yl(y) \le l(x) \le x < y

所以,c[x]c[x] 真包含于 c[x+lowbit(x)]c[x + \operatorname{lowbit}(x)]

性质 33:对于任意 x<y<x+lowbit(x)\boldsymbol{x < y < x + \operatorname{lowbit}(x)},有 c[x]\boldsymbol{c[x]}c[y]\boldsymbol{c[y]} 不交。

证明

证明:设 x=s×2k+1+2kx = s \times 2^{k + 1} + 2^k,则 y=x+b=s×2k+1+2k+by = x + b = s \times 2^{k + 1} + 2^k + b,其中 1b<2k1 \le b < 2^k

不难发现 lowbit(y)=lowbit(b)\operatorname{lowbit}(y) = \operatorname{lowbit}(b)。又因为 blowbit(b)0b - \operatorname{lowbit}(b) \ge 0

因此 l(y)=ylowbit(y)+1=x+blowbit(b)+1>xl(y) = y - \operatorname{lowbit}(y) + 1 = x + b - \operatorname{lowbit}(b) + 1 > x,即 l(x)x<l(y)yl(x) \le x < l(y) \le y

所以,c[x]c[x]c[y]c[y] 不交。

有了这三条性质的铺垫,我们接下来看树状数组的树形态(请忽略 aacc 的连边)。

示意图
图示

事实上,树状数组的树形态是 xxx+lowbit(x)x + \operatorname{lowbit}(x) 连边得到的图,其中 x+lowbit(x)x + \operatorname{lowbit}(x)xx 的父亲。

注意,在考虑树状数组的树形态时,我们不考虑树状数组大小的影响,即我们认为这是一棵无限大的树,方便分析。实际实现时,我们只需用到 xnx \le nc[x]c[x],其中 nn 是原数组长度。

这棵树天然满足了很多美好性质,下面列举若干(设 fa[u]fa[u] 表示 uu 的直系父亲):

  • u<fa[u]u < fa[u]
  • uu 大于任何一个 uu 的后代,小于任何一个 uu 的祖先。
  • uulowbit\operatorname{lowbit} 严格小于 fa[u]fa[u]lowbit\operatorname{lowbit}

证明

y=x+lowbit(x)y = x + \operatorname{lowbit}(x)x=s×2k+1+2kx = s \times 2^{k + 1} + 2^k,则 y=(s+1)×2k+1y = (s + 1) \times 2^{k +1},不难发现 lowbit(y)2k+1>lowbit(x)\operatorname{lowbit}(y) \ge 2^{k + 1} > \operatorname{lowbit}(x),证毕。

  • xx 的高度是 log2lowbit(x)\log_2\operatorname{lowbit}(x),即 xx 二进制最低位 1 的位数。

高度的定义

xx 的高度 h(x)h(x) 满足:如果 xmod2=1x \bmod 2 = 1,则 h(x)=0h(x) = 0,否则 h(x)=max(h(y))+1h(x) = \max(h(y)) + 1,其中 yy 代表 xx 的所有儿子(此时 xx 至少存在一个儿子 x1x - 1)。

也就是说,一个点的高度恰好比它最高的那个儿子再高 11。如果一个点没有儿子,它的高度是 00

这里引出高度这一概念,是为后面解释复杂度更方便。

  • c[u]c[u] 真包含于 c[fa[u]]c[fa[u]](性质 22)。
  • c[u]c[u] 真包含于 c[v]c[v],其中 vvuu 的任一祖先(在上一条性质上归纳)。
  • c[u]c[u] 真包含 c[v]c[v],其中 vvuu 的任一后代(上面那条性质 uuvv 颠倒)。
  • 对于任意 v>uv' > u,若 vv' 不是 uu 的祖先,则 c[u]c[u]c[v]c[v'] 不交。

证明

uuuu 的祖先中,一定存在一个点 vv 使得 v<v<fa[v]v < v' < fa[v],根据性质 33c[v]c[v'] 不相交于 c[v]c[v],而 c[v]c[v] 包含 c[u]c[u],因此 c[v]c[v'] 不交于 c[u]c[u]

  • 对于任意 v<uv < u,如果 vv 不在 uu 的子树上,则 c[u]c[u]c[v]c[v] 不交(上面那条性质 uuvv' 颠倒)。
  • 对于任意 v>uv > u,当且仅当 vvuu 的祖先,c[u]c[u] 真包含于 c[v]c[v](上面几条性质的总结)。这就是树状数组单点修改的核心原理。
  • u=s×2k+1+2ku = s \times 2^{k + 1} + 2^k,则其儿子数量为 k=log2lowbit(u)k = \log_2\operatorname{lowbit}(u),编号分别为 u2t(0t<k)u - 2^t(0 \le t < k)
    • 举例:假设 k=3k = 3uu 的二进制编号为 ...1000,则 uu 有三个儿子,二进制编号分别为 ...0111...0110...0100

证明

在一个数 xx 的基础上减去 2t2^txx 二进制第 tt 位会反转,而更低的位保持不变。

考虑 uu 的儿子 vv,有 v+lowbit(v)=uv + \operatorname{lowbit}(v) = u,即 v=u2tv = u - 2^tlowbit(v)=2t\operatorname{lowbit}(v) = 2^t。设 u=s×2k+1+2ku = s \times 2^{k + 1} + 2^k

考虑 0t<k\boldsymbol{0 \le t < k}uu 的第 tt 位及后方均为 00,所以 v=u2tv = u - 2^t 的第 tt 位变为 11,后面仍为 00满足 lowbit(v)=2t\operatorname{lowbit}(v) = 2^t

考虑 t=k\boldsymbol{t = k},则 v=u2kv = u - 2^kvv 的第 kk 位变为 00不满足 lowbit(v)=2t\operatorname{lowbit}(v) = 2^t

考虑 t>k\boldsymbol{t > k},则 v=u2tv = u - 2^tvv 的第 kk 位是 11,所以 lowbit(v)=2k\operatorname{lowbit}(v) = 2^k不满足 lowbit(v)=2t\operatorname{lowbit}(v) = 2^t

  • uu 的所有儿子对应 cc 的管辖区间恰好拼接成 [l(u),u1][l(u), u - 1]
    • 举例:假设 k=3k = 3uu 的二进制编号为 ...1000,则 uu 有三个儿子,二进制编号分别为 ...0111...0110...0100
    • c[...0100] 表示 a[...0001 ~ ...0100]
    • c[...0110] 表示 a[...0101 ~ ...0110]
    • c[...0111] 表示 a[...0111 ~ ...0111]
    • 不难发现上面是三个管辖区间的并集恰好是 a[...0001 ~ ...0111],即 [l(u),u1][l(u), u - 1]

证明

uu 的儿子总能表示成 u2t(0t<k)u - 2^t(0 \le t < k),不难发现,tt 越小,u2tu - 2^t 越大,代表的区间越靠右。我们设 f(t)=u2tf(t) = u - 2^t,则 f(k1),f(k2),,f(0)f(k - 1), f(k - 2), \ldots, f(0) 分别构成 uu 从左到右的儿子。

不难发现 lowbit(f(t))=2t\operatorname{lowbit}(f(t)) = 2^t,所以 l(f(t))=u2t2t+1=u2t+1+1l(f(t)) = u - 2^t - 2^t + 1 = u - 2^{t + 1} + 1

考虑相邻的两个儿子 f(t+1)f(t + 1)f(t)f(t)。前者管辖区间的右端点是 f(t+1)=u2t+1f(t + 1) = u - 2^{t + 1},后者管辖区间的左端点是 l(f(t))=u2t+1+1l(f(t)) = u - 2^{t + 1} + 1,恰好相接。

考虑最左面的儿子 f(k1)f(k - 1),其管辖左边界 l(f(k1))=u2k+1l(f(k - 1)) = u - 2^k + 1 恰为 l(u)l(u)

考虑最右面的儿子 f(0)f(0),其管辖右边界就是 u1u - 1

因此,这些儿子的管辖区间可以恰好拼成 [l(u),u1][l(u), u - 1]

单点修改

现在来考虑如何单点修改 a[x]a[x]

我们的目标是快速正确地维护 cc 数组。为保证效率,我们只需遍历并修改管辖了 a[x]a[x] 的所有 c[y]c[y],因为其他的 cc 显然没有发生变化。

管辖 a[x]a[x]c[y]c[y] 一定包含 c[x]c[x](根据性质 11),所以 yy 在树状数组树形态上是 xx 的祖先。因此我们从 xx 开始不断跳父亲,直到跳得超过了原数组长度为止。

nn 表示 aa 的大小,不难写出单点修改 a[x]a[x] 的过程:

  • 初始令 x=xx' = x
  • 修改 c[x]c[x']
  • xx+lowbit(x)x' \gets x' + \operatorname{lowbit}(x'),如果 x>nx' > n 说明已经跳到尽头了,终止循环;否则回到第二步。

区间信息和单点修改的种类,共同决定 c[x]c[x'] 的修改方式。下面给几个例子:

  • c[x]c[x'] 维护区间和,修改种类是将 a[x]a[x] 加上 pp,则修改方式则是将所有 c[x]c[x'] 也加上 pp
  • c[x]c[x'] 维护区间积,修改种类是将 a[x]a[x] 乘上 pp,则修改方式则是将所有 c[x]c[x'] 也乘上 pp

然而,单点修改的自由性使得修改的种类和维护的信息不一定是同种运算,比如,若 c[x]c[x'] 维护区间和,修改种类是将 a[x]a[x] 赋值为 pp,可以考虑转化为将 a[x]a[x] 加上 pa[x]p - a[x]。如果是将 a[x]a[x] 乘上 pp,就考虑转化为 a[x]a[x] 加上 a[x]×pa[x]a[x] \times p - a[x]

下面以维护区间和,单点加为例给出实现。

void add(int x, int k) {
  while (x <= n) {  // 不能越界
    c[x] = c[x] + k;
    x = x + lowbit(x);
  }
}

建树

也就是根据最开始给出的序列,将树状数组建出来(cc 全部预处理好)。

一般可以直接转化为 nn 次单点修改,时间复杂度 Θ(nlogn)\Theta(n \log n)(复杂度分析在后面)。

比如给定序列 a=(5,1,4)a = (5, 1, 4) 要求建树,直接看作对 a[1]a[1] 单点加 55,对 a[2]a[2] 单点加 11,对 a[3]a[3] 单点加 44 即可。

复杂度分析

空间复杂度显然 Θ(n)\Theta(n)

时间复杂度:

  • 对于区间查询操作:整个 xxlowbit(x)x \gets x - \operatorname{lowbit}(x) 的迭代过程,可看做将 xx 二进制中的所有 11,从低位到高位逐渐改成 00 的过程,拆分出的区间数等于 xx 二进制中 11 的数量(即 popcount(x)\operatorname{popcount}(x))。因此,单次查询时间复杂度是 Θ(logn)\Theta(\log n)
  • 对于单点修改操作:跳父亲时,访问到的高度一直严格增加,且始终有 xnx \le n。由于点 xx 的高度是 log2lowbit(x)\log_2\operatorname{lowbit}(x),所以跳到的高度不会超过 log2n\log_2n,所以访问到的 cc 的数量是 logn\log n 级别。因此,单次单点修改复杂度是 Θ(logn)\Theta(\log n)

例题:

On this page