Data structures · Competitive programming

Fenwick Trees:
four powerful patterns

A Binary Indexed Tree is more than a fast prefix-sum structure. With a small change in what we store - and how we interpret updates - it can handle point updates, range updates, two-dimensional data, and more.

O(log n) operationsPrefix sumsDifference arrays2D grids
01

Point update, range query

The classic Fenwick Tree pattern.

What it supports

Change one array position by a given amount, then ask for the sum over any interval. The structure stores partial sums arranged so that a prefix can be assembled from only a logarithmic number of tree entries.

sum(l … r) = prefix(r) − prefix(l − 1)

A range is reduced to the difference of two prefixes.

Example array

Imagine the current values are:

index 14
index 22
index 37
index 41
index 53

To query indices 2–4, calculate the prefix through 4 and subtract the prefix through 1. The result is 2 + 7 + 1 = 10.

Update(i, Δ)Add Δ to one position and to each tree entry responsible for that position.
Prefix(i)Walk through the relevant tree entries, moving to smaller covered ranges.
Range(l, r)Subtract two prefix sums to isolate the desired interval.
Use it when: values change one at a time and queries ask for sums over intervals. This is the standard choice for dynamic prefix/range sums.
02

Range update, point query

Store changes, not the full array.

The key idea: differences

Instead of representing each array value directly, represent how the value changes from one position to the next. Adding a value Δ to every element in an interval only changes two boundaries in this difference representation.

add Δ at l · add −Δ at r + 1

These two boundary changes mean that the extra Δ begins at l and stops after r.

Boundary picture

Suppose we add +5 to positions 2 through 4:

0before 1
+5at 2
0at 3
0at 4
−5at 5

The difference structure records +5 at the start and −5 immediately after the end. A prefix sum of these differences tells us the accumulated change at any position.

Range addRecord the positive change at the left boundary.
Close the intervalRecord the opposite change just after the right boundary.
Point queryTake the prefix sum of the difference structure at that index, then combine it with the original value if one is being maintained separately.
Important: if the initial array is not all zeros, its original value must also be accounted for. The BIT described here stores the accumulated updates.
03

Range update, range query

Two Fenwick Trees — what they store, and why it works.

Goal: add a value to every element in an interval, and later calculate the sum of any interval. We will build this from the difference-array idea rather than starting with a formula to memorise.

Step 1 — Represent the array by its changes

Let D[i] = A[i] − A[i−1], with A[0] = 0. In other words, D[i] records how much the array changes when we move from position i−1 to position i.

The original value at position i is the sum of all changes up to i:

A[i] = D[1] + D[2] + ··· + D[i]

This is exactly a prefix sum, so a BIT storing D can answer point-value queries.

Now consider adding +3 to positions 2 through 4 of an initially zero array:

index1
index2
index3
index4
index5
A0
A3
A3
A3
A0

Only two differences change: D[2] increases by 3, and D[5] decreases by 3. The second change cancels the addition immediately after the interval.

D[2] += 3    and    D[5] −= 3

Step 2 — Why one BIT is not enough for range sums

To find A[i], we sum the differences up to i. But a prefix sum of array values adds those answers together:

P[x] = A[1] + A[2] + ··· + A[x]

Think about one difference D[i]. It contributes to every array value from A[i] onwards. Within the prefix ending at x, it is therefore counted once for A[i], once for A[i+1], and so on through A[x] — a total of x − i + 1 times.

So each difference contributes its value multiplied by the number of times it appears:

P[x] = Σ (x − i + 1) · D[i], for i = 1 … x

Split the multiplier into two parts: x − i + 1 = x − (i − 1). Distributing gives:

P[x] = x · ΣD[i] − Σ((i − 1) · D[i])

That split is the whole trick. We need two prefix sums: the sum of D[i], and the sum of (i−1)·D[i].

BIT 1 — stores the differences

At each index i, the underlying value is D[i]. Its prefix query gives the sum of differences through x, which is the current array value A[x].

B₁ stores D[i]

Its prefix sum supplies the first quantity in the prefix-sum formula.

BIT 2 — stores weighted differences

At each index i, the underlying value is the difference multiplied by one less than its index: (i−1)·D[i].

B₂ stores (i − 1) · D[i]

Its prefix sum supplies the correction term that accounts for how long each difference contributes.

Step 3 — Put the two prefix sums together

Let sum(B, x) mean “the prefix sum through x in BIT B”. The derivation above now becomes a direct recipe:

P[x] = x · sum(B₁, x) − sum(B₂, x)

To get the sum from l through r, subtract the prefix ending just before l:

sum(l … r) = P[r] − P[l − 1]

Each interval addition updates both BITs at the same two difference boundaries. At a boundary i, if the difference changes by Δ, BIT 1 receives Δ and BIT 2 receives (i−1)·Δ. Each boundary update costs O(log n).

Worked check — add +3 to positions 2 through 4

After the update, D[2] = +3 and D[5] = −3; all other differences are zero. Thus BIT 1's underlying values are +3 at index 2 and −3 at index 5. BIT 2's underlying values are (2−1)·3 = +3 at index 2 and (5−1)·(−3) = −12 at index 5.

Prefix xsum(B₁, x)sum(B₂, x)P[x] = x·sum(B₁,x) − sum(B₂,x)
3333 × 3 − 3 = 6
4334 × 3 − 3 = 9
50−95 × 0 − (−9) = 9

Check against the actual array [0, 3, 3, 3, 0]: its prefix sums at positions 3, 4, and 5 are 6, 9, and 9. The formula matches each one.

What exactly happens when an interval update reaches the two BITs?

For an addition of Δ on [l, r], the difference array receives +Δ at l and −Δ at r+1. For each of those two changes, update BIT 1 with the difference change itself, and BIT 2 with the change multiplied by (index−1). Both BITs are ordinary Fenwick Trees; only the values supplied to them differ.

Indexing matters: this page uses one-based indices, D[i] = A[i]−A[i−1], and BIT 2 stores (i−1)·D[i]. With these exact definitions, the prefix formula is P[x] = x·sum(B₁,x) − sum(B₂,x). Other conventions are possible, but their formulas must change consistently.
04

2D Fenwick Tree

The same idea, extended to a grid.

What changes?

Instead of an array, imagine a matrix with rows and columns. A 2D BIT stores partial sums over rectangular regions aligned with its index structure. Updating one cell and querying a rectangle both take logarithmic time in each dimension.

rectangle = P(x₂,y₂) − P(x₁−1,y₂)
− P(x₂,y₁−1) + P(x₁−1,y₁−1)

P(x, y) is the sum of the rectangle from the top-left corner through (x, y).

Visualise a prefix rectangle

·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·

Green cells illustrate a top-left prefix region.

Point updateWalk through affected Fenwick indices in the row dimension and, inside each, the column dimension.
Prefix queryAccumulate the relevant 2D tree entries while moving down through both index dimensions.
Rectangle queryUse inclusion–exclusion on four prefix rectangles to keep only the requested rectangle.
Typical uses: dynamic grid sums, image-like matrices, board simulations, and counting points inside axis-aligned rectangles. A basic 2D BIT supports point updates and rectangle-sum queries; range updates in two dimensions require a more elaborate construction.
∑

Complexity analysis

Assuming standard Fenwick operations and a matrix with R rows and C columns.

PatternUpdateQueryExtra space
Point update, range queryO(log n)O(log n)O(n)
Range update, point queryO(log n)O(log n)O(n)
Range update, range queryO(log n) per BIT; two BITs totalO(log n) per prefix; two prefixes per rangeO(n)
2D BIT: point update, rectangle queryO(log R · log C)O(log R · log C) per prefix; four prefixes per rectangleO(RC)

Big-O suppresses constant factors. A range query may perform multiple prefix queries, but a constant number of logarithmic operations remains logarithmic.

Quick comparison

NeedChooseCore trick
Change one element; sum an intervalPattern 1Prefix sums and subtraction
Add to an interval; read one elementPattern 2Difference array + one BIT
Add to an interval; sum an intervalPattern 3Weighted differences + two BITs
Change one grid cell; sum a rectanglePattern 42D prefix sums + inclusion–exclusion

Final mental model

Fenwick Trees are efficient because they maintain carefully chosen partial aggregates. For interval updates, difference arrays turn a whole range change into two boundary events. For interval sums after interval updates, a second weighted tree supplies the extra information needed to reconstruct prefixes. In two dimensions, the same prefix-and-subtraction principle applies to rectangles.