Binary Indexed Tree (BIT)

蟻本 p.159

https://www.slideshare.net/hcpc_hokudai/binary-indexed-tree

概要

Binary Indexed Tree (BIT) または Fenwick Tree は、累積和に更新操作を加えたデータ構造。
セグメント木の機能を削り、実装を単純化したものともみなせる。

\(n\) 個の要素について、次の操作を行うことができる。

  • 初期化: \(\mathcal{O}(n)\)

  • 要素1つに対する加算: \(\mathcal{O}(\log{n})\)

  • 部分和を求める問い合わせ: \(\mathcal{O}(\log{n})\)

部分和では区間 \([1, r]\) の和が求まる。
任意の区間 \([l, r]\) の和を求めるには、累積和と同様に \([1, r]\) の部分和から \([1, l-1]\) の部分和を引けばよい。

原理

BITの加算操作・更新操作ではセグメント木と同様に、ノード間の移動が発生する。
セグメント木ではこれをビットシフトや再帰で計算していたが、BITではノードの添え字を2進数で表したときの“1”であるビットの位置を利用して計算する。
ノード間の移動では特に、「最下位が“1”であるビットの位置」を順次取得することになる。
これは符号付き2進数を用いて \(a \land -a\) で求めることができる。
\[\begin{split}\begin{eqnarray} 6 \land -6 &=& 0110_{(2)} \land 1010_{(2)} \\ &=& 0010_{(2)} \\ \end{eqnarray}\end{split}\]

\(a \land -a\) の計算結果はビットが1か所だけ立つ。この場所が「最下位が“1”であるビットの位置」となる。

実装

BITは1-indexedで値を保持することに注意。

[1]:
def bit_init(a):
    """BITの初期化"""
    n = len(a) + 1
    bit = [0] * n
    for i in range(n-1):
        bit_add(bit, i+1, a[i])
    return bit

def bit_sum(bit, i):
    """[0, i)の区間和を求める(1-indexed)"""
    s = 0
    while i > 0:
        s += bit[i]
        i -= i & -i
    return s

def bit_add(bit, i, x):
    """i番目の値をxだけ増加させる(1-indexed)"""
    while i < len(bit):
        bit[i] += x
        i += i & -i
[2]:
A = [3, 5, 7, -4, 2, -3, 4]
B = bit_init(A)
print(bit_sum(B, 4))
bit_add(B, 4, 5)
bit_add(B, 5, 6)
print(bit_sum(B, 4))
11
16