BIT computes prefix sums and updates individual elements in \(O(\log n)\) time, using only \(O(n)\) space.
Introduction
Binary Indexed Tree (BIT, or Fenwick Tree) maintains prefix sums of an array and supports adding a value to one element. Here we use ordinary addition: sum(i) returns a[1] + ... + a[i], and add(i, v) increases a[i] by v.
Unlike Segment Tree, Fenwick Tree directly queries prefixes. For addition, we can still query any range [l, r] by subtracting two prefixes: sum(r) - sum(l - 1). Other operations need their own algebraic conditions; associativity alone does not guarantee that the same query and update code works.
Fenwick Tree uses n + 1 array slots (index 0 is unused). An iterative segment tree commonly uses 2*n slots, and a recursive implementation commonly allocates about 4*n; all use \(O(n)\) space.
Fenwick Tree has two functions. They are sum and add, as shown below.
Which interval does each node store?
As in the segment tree diagrams, place each stored sum above the part of the original array it covers. Let lowbit(i) = i & -i: it is the value of the least significant set bit, and also the length of the interval stored at node i.
Each bar represents one stored sum. Its label is the BIT index, and its horizontal span is the interval it covers.
The invariant is: tree[i] stores the sum of a[i - lowbit(i) + 1 ... i]. Here a means the original array; tree means the BIT storage called A in the code below. The numbers in the diagram are indices, not array values or sums.
tree[6]:6 = 0b0110, solowbit(6) = 2; it stores[5, 6].tree[12]:12 = 0b1100, solowbit(12) = 4; it stores[9, 12].tree[8]:8 = 0b1000, solowbit(8) = 8; it stores[1, 8].
Unlike a full segment tree, the BIT does not store both halves at every split: for example, it stores [1, 8] but has no single node for [9, 16]. It stores one block ending at each index, and the binary representation tells us how to move between blocks. The example uses n = 16 to make the pattern visible; n need not be a power of two.
add increase an element
| |
An update must change every stored interval containing the modified index. For add(5, ฮ), these are [5, 5], [5, 6], [1, 8], and [1, 16].
Updating a[5] changes the four highlighted sums. Each highlighted interval contains index 5.
i += lowbit(i) jumps to the next larger stored interval containing the current interval. In binary, adding the lowest set bit carries into a higher bit: 0101 โ 0110 โ 1000 โ 10000. The new block’s right endpoint moves right, while its left endpoint stays put or moves left, so it still contains the updated element. Nodes between these jumps do not contain that element.
With n = 16, the loop visits 5 โ 6 โ 8 โ 16, adds ฮ to each stored sum, and stops when the next index is 32. Elements are 1-indexed; A[0] is unused, and add requires 1 <= i <= n (starting at 0 would never advance).
sum get a prefix sum
| |
To query a prefix, take the block ending at i, then continue immediately to its left. Because the block starts at i - lowbit(i) + 1, the remaining prefix ends at i - lowbit(i). This is exactly why the loop subtracts lowbit(i).
sum(13) reads nodes 13, 12, and 8. Their blocks tile [1, 13] without overlap or gaps.
For sum(13), the steps are:
Current i | lowbit(i) | Read this block | Next i |
|---|---|---|---|
13 (1101) | 1 | tree[13]: [13, 13] | 12 |
12 (1100) | 4 | tree[12]: [9, 12] | 8 |
8 (1000) | 8 | tree[8]: [1, 8] | 0 (stop) |
Thus sum(13) = tree[13] + tree[12] + tree[8]. For a concrete check, if a[i] = i, these stored sums are 13, 42, and 36, giving 91, the sum of integers 1 through 13.
Subtracting lowbit clears one set bit each time: 1101 โ 1100 โ 1000 โ 0000. A query therefore visits as many nodes as there are set bits in its index, at most \(O(\log n)\). In an update, the lowest set bit moves strictly higher at each jump, so updates also take \(O(\log n)\) time. sum requires 0 <= i <= n; sum(0) returns 0, making the range formula work even when l = 1.
template
| |
The constructor initializes all stored sums to zero. To build from an existing array, call add(i, a[i]) for each index 1 ... n; this simple construction takes \(O(n\log n)\) time. To assign a new value rather than add a delta, keep the original values and call add(i, new_value - a[i]), then update a[i]. Choose a numeric type large enough for all intermediate sums (for example, long long for integer sums that may exceed int).