Segment Tree

Prefix sums answer range sums in O(1), until the array changes and you must rebuild them in O(n). A segment tree handles both range queries and updates in O(log n), which is why it shows up in leaderboards, monitoring dashboards and half of all contest problems.

advanced⏱ 15 min read
01

Learn

The idea, the mechanics and the cost.

01A tree of range sums

Put the array in the leaves of a complete binary tree. Every internal node stores the sum of its two children, so each node is responsible for one contiguous range: the root covers everything, its children cover the halves, and so on.

To answer sum(l..r), combine the few nodes whose ranges lie fully inside [l, r] and together tile it exactly. There are at most two such nodes per level, so a query touches O(log n) nodes.

In the figure, sum(2..6) uses the nodes [2–3], [4–5] and [6]: three nodes instead of five numbers. On a million elements it is about 40 nodes instead of a million.

Store the tree in an array of size 4n: node v has children 2v and 2v+1, just like a binary heap. (Exactly 2n is enough only when n is a power of two, or with the iterative bottom-up layout.)

36[0–7]15[0–3]21[4–7]7[0–1]8[2–3]9[4–5]12[6–7]5[0]2[1]7[2]1[3]3[4]6[5]4[6]8[7]sum(2..6) = 8 + 9 + 4 = 21: three nodes instead of five numbers
Each node shows its sum and, below it, the range it covers.

02Point updates and other operations

To change a[i], update its leaf and then walk up to the root, recomputing each ancestor as the sum of its two children. Only the log n + 1 nodes on that path change.

Nothing here depends on the operation being +. Any associative operation works: minimum, maximum, gcd, xor, even "maximum subarray sum" if each node stores a few extra numbers. Swap the combine function and keep the rest.

With lazy propagation, a node can also remember a pending update for its whole range ("add 5 to all of [l, r]") and pass it down only when needed. That gives range updates in O(log n) as well. Learn the basic version first.

36[0–7]15[0–3]21[4–7]7[0–1]8[2–3]9[4–5]12[6–7]5[0]2[1]7[2]1[3]3[4]6[5]4[6]8[7]a[4] += 5: only the path from leaf to root changes, log₂8 + 1 = 4 nodes

03Writing it: the recursive version

The usual contest implementation is recursive. build(v, l, r): if l == r, store a[l]; otherwise build both halves at m = (l + r) / 2 and set t[v] = t[2v] + t[2v+1].

query(v, l, r, ql, qr) has three cases:

  • [l, r] lies outside [ql, qr]: return 0;
  • [l, r] lies fully inside [ql, qr]: return t[v] at once;
  • otherwise: return the sum of both children.

update(v, l, r, i, x) goes down to the leaf of i and recomputes t[v] on the way back up. Every call costs O(log n).

If you only need sums with point updates, the shorter Fenwick tree from its own lesson in the Trees stage is enough; the segment tree is the tool when you need min, max or anything else that cannot be "subtracted".

Cost at a glance

BuildO(n)
Range queryO(log n)
Point updateO(log n)
MemoryO(n)

Remember

  1. Each segment tree node stores the aggregate of one contiguous range, and any range splits into O(log n) such nodes.
  2. A point update changes one leaf and its ancestors only, so it also costs O(log n).
  3. Any associative operation works (sum, min, max, gcd): change the combine function and keep the rest.
02

Play

Step through the algorithm, then try it on your own input.

👀 What to watch: During the query, count the highlighted nodes: far fewer than the numbers in the range. During the update, only one path from root to leaf lights up.

Interactive visualizerfocus here, then space ← →
✎ Your input2–8 numbers (padded with zeros to a power of two); query indices are 0-based and inclusive.

Segment tree (sum)

0[0,7]0[0,3]0[4,7]0[0,1]0[2,3]0[4,5]0[6,7]0[0,0]0[1,1]0[2,2]0[3,3]0[4,4]0[5,5]0[6,6]0[7,7]

Array

52713648
A segment tree answers range-sum queries and point updates in O(log n). Each node stores the sum of a contiguous range.

Pseudocode

 1 leaves hold the array; each internal node = sum of its two children 2 build: fill leaves, then t[v] = t[2v] + t[2v+1] bottom-up 3 query(l,r): combine O(log n) canonical nodes that tile the range 4   take a node fully inside the range; recurse otherwise 5 update(i): change the leaf, then refresh its ancestors
1 / 1
03

Check

Three questions. Pick an answer to see why.

Q1

You need range minimum queries on an array that also receives point updates. What fits best?

Q2

In a segment tree over 16 elements, how many nodes change when you update one element?

Q3

In the recursive query, the current node covers [l, r] and lies fully inside [ql, qr]. What does it do?

04

Practice

Real problems to lock it in, easiest first.