A Segment Tree is a tree-based data structure used to efficiently handle range-based queries on an array. It allows us to query information (such as sum, minimum, or maximum) over a subarray and also update elements efficiently.
In simple terms, a Segment Tree is used when:
Consider an array: arr = [a₁, a₂, a₃, a₄, …, aₙ]
Suppose we have two types of queries:
Using a prefix sum array, we can answer range sum queries in O(1) time. However, when an update happens:
O(n) timeIf there are many update queries, the total time complexity becomesO(q · n), which is not feasible for large inputs (e.g., n = 10⁶).
This leads to TLE (Time Limit Exceeded).
A Segment Tree solves this problem efficiently by:
O(log n)O(log n)So, even if we have many queries:
O(q log n)That is why Segment Trees are widely used in competitive programming and high-performance systems.
We can take a divide-and-conquer approach when it comes to array segments. We compute and store the sum of the elements of the whole array, i.e., the sum of the segment a[0 … n - 1]
We then split the array into two halves a[0 … (n - 1)/2] and a[(n + 1)/2 … n - 1] and compute the sum of each half and store them. Each of these two halves is again split into two smaller halves, and this process continues until all segments reach size 1.
These segments can be viewed as forming a binary tree. The root of this tree represents the segment a[0 … n - 1], and each internal node has exactly two children. This is why the data structure is called a Segment Tree, even though in most implementations the tree is not constructed explicitly.
Below is a visual representation of a Segment Tree built over the array: a = [1, 3, -2, 8, -7]

The height of a Segment Tree is O(log n), because when moving from the root to the leaves, the size of the segment is reduced by approximately half at each level.
Before constructing a Segment Tree, we need to decide two important things:
[l, r].A node is called a leaf node if its segment represents only a single element of the array. In that case, the value stored in the node is simply the corresponding array element.
To build the Segment Tree, we start from the leaf nodes and assign their values. Using the merge operation, we then compute the values of their parent nodes. This process continues until we reach the root node, which represents the entire array.
In practice, the construction is described recursively in the opposite direction, starting from the root node:
By starting this process from the root node, we are able to construct the complete Segment Tree.
The time complexity of building a Segment Tree is O(n), assuming the merge operation takes constant time, since each node is processed exactly once.
To compute the sum of a range [l, r], we traverse the Segment Tree and use the precomputed sums stored in the nodes.
Since only a limited number of nodes are visited at each level and the height of the tree is O(log n), the total time complexity is O(log n).
An update query modifies a single element in the array. We recursively traverse the tree to the leaf node representing that index and update its value.
While returning back up the tree, we recompute the values of all affected parent nodes using the merge operation.
Since only one node per level is updated, the time complexity of an update query is also O(log n).
There are may ways to implement a Segment Tree. One common approach is to use an array-based representation, where the tree is stored in a flat array. This method is efficient in terms of both time and space. if want to know why we use array to represent segment tree please refer to the CP-Algorithms.
In an array-based representation, the Segment Tree is stored in an array of size 4 * n to accommodate all nodes. The root node is at index 0, and for any node at index i:
2 * i + 12 * i + 2This Array-Based Representation allows for efficient traversal and updates without the need for explicit pointers.
idx → index of the current node in the segment tree arrayl → left boundary of the segment represented by the current noder → right boundary of the segment represented by the current nodeql → left boundary of the query updateqr → right boundary of the query updatepos → index of the element for a point updateval → value to be added during a point updateThe build function constructs the segment tree using a recursive divide-and-conquer strategy. It takes the current segment tree index idx, the array range [l, r], and the input array arr.
When l == r, the recursion reaches a leaf node, representing a single element of the array. At this point, the value arr[l] is directly stored in seg[idx].
Otherwise, the current range is split at mid into two subranges:[l, mid] and [mid + 1, r]. The function then recursively builds the left child at 2 * idx + 1 and the right child at 2 * idx + 2.
After both children are constructed, the current node aggregates their values. In this implementation, the aggregation operation is sum:seg[idx] = seg[2 * idx + 1] + seg[2 * idx + 2]. This process continues upward until the root node is built.
The rangeSumQuery function is used to compute the sum of elements within a given query range [ql, qr] using the segment tree. It operates on a node at index idx representing the segment [l, r].
To answer a query efficiently, the algorithm evaluates three possible overlap cases between the current segment and the query range.
[l, r] lies completely outside the query range [ql, qr], the function returns 0, since it contributes nothing to the sum.[l, r] is fully contained within the query range [ql, qr], the precomputed value seg[idx] is returned directly. This avoids unnecessary recursion.mid into two halves:[l, mid] and [mid + 1, r]. The query is executed recursively on both children, and the final result is the sum of the left and right subqueries.This operation is read-only. The segment tree is not modified during a range sum query, as no updates are required. The result is obtained purely through traversal and aggregation.
The pointUpdate operation updates the value at a specific array index and reflects this change in the segment tree. It takes five parameters: the target index, the new value val, the segment tree index idx, and the segment range [l, r].
The update process follows a binary-search–like traversal. At each recursive step, the current segment [l, r] is divided at mid to determine whether the target index lies in the left subtree [l, mid] or the right subtree [mid + 1, r].
This process continues recursively until l === r, which represents a leaf node. This node corresponds exactly to the update index, and its value is increased by val.
While returning from recursion, each ancestor node is recomputed to maintain correctness of the tree. For a sum segment tree, the parent value is updated as:seg[idx] = seg[2 * idx + 1] + seg[2 * idx + 2]
This ensures that all affected ranges reflect the updated index.
Here is a sample code snippet for Segment Tree sum operations:
I have explained Segment Trees based on my own understanding and what I learned while studying them. I hope this guide is helpful to you. If you notice any mistakes or areas for improvement, please feel free to report them.
You can practice Segment Tree problems on various competitive programming platforms. Here are a few recommended problems to get you started:
These problems will help you understand the implementation and application of Segment Trees in various scenarios. Happy coding!
For more in-depth information on Segment Trees, consider exploring the following resources:
📌 Note:- If this link does not open, navigate via Codeforces → EDU → Courses → ITMO Academy: Pilot Course → Segment Tree, Part 1These resources will provide you with a deeper understanding of Segment Trees and their applications in competitive programming and algorithm design.