Note: This section assumes that you already understand the basics of Segment Trees. If not, please read the Segment Tree explanation first and then come back here.
👉 Go to Segment Tree
Lazy Propagation is an optimization technique used in Segment Trees to efficiently handle range update operations. Instead of updating all elements in a range immediately, updates are delayed and applied only when required.
Lazy Propagation is mainly used when we need to perform range-based updates. For example:
[ql, qr]Without lazy propagation, these operations would require updating every element in the range, leading to poor performance.
As the name suggests, lazy propagation works by postponing updates. When a range update is applied, the Segment Tree node stores the update value in a separate lazy array instead of immediately updating its children.
For example, if we need to add 7 to the range [5, 8], the node covering this range will store:
lazy[idx] = 7
The actual update is applied only when this segment is required during a query or further traversal.
This avoids unnecessary updates and ensures that operations remain efficient.
Using a prefix sum array, we can answer range sum queries in O(1) time. However, when an update occurs:
O(n) timeWith many updates, the total time complexity becomes O(q · n), which leads to TLE for large inputs.
A Segment Tree with Lazy Propagation solves this problem efficiently by:
O(log n)O(log n)Even with a large number of queries, the total time complexity becomes O(q · log n), which is efficient and scalable.
Lazy propagation turns expensive range updates into efficient operations, making segment trees practical for real competitive programming problems.
Lazy Propagation is an extension of the Segment Tree that is used when range-based update operations are required. The core structure of the Segment Tree remains the same; lazy propagation only changes how and when updates are applied.
Along with the segment tree array, an additional array called thelazy array is maintained. This array stores pending updates for segments that have not yet been propagated to their child nodes.
When a range update completely covers a segment, the update is applied to the current node, and the update value is stored in thelazy array. The children are not updated immediately.
Updates are intentionally delayed. A segment is only updated when it is required during a query or when further traversal of that segment is necessary. This avoids repeated updates on large ranges.
Lazy propagation postpones updates until they are actually needed, while still keeping the segment tree logically correct.
Whenever a node is accessed, any pending lazy value stored at that node is first applied. The value is then passed to its children, and the lazy value of the current node is cleared.
This ensures correctness while maintaining logarithmic time complexity.
The construction of a Segment Tree with Lazy Propagation is identical to a normal Segment Tree. The tree is built using a merge operation, and the lazy array is initialized with zeros.
O(n)O(log n)O(log n)O(log n)By deferring updates and applying them only when necessary, Lazy Propagation allows Segment Trees to efficiently handle large numbers of range updates and queries.
In a Segment Tree with Lazy Propagation, sum queries are handled similarly to a normal Segment Tree, with one additional step to ensure correctness.
[ql, qr], its stored sum is returned directly.Since lazy updates are applied only when required and the height of the tree is O(log n), the time complexity of a sum query remains O(log n).
A point update modifies a single element in the array. The process is similar to a normal Segment Tree update, with the additional handling of lazy values.
Only one node per level is updated, so the time complexity of a point update is also O(log n).
Range update queries are the main reason for using Lazy Propagation. Instead of updating every element in the range immediately, updates are deferred.
lazy array.By postponing updates and applying them only when necessary, range updates are performed in O(log n) time instead of linear time.
This combination of efficient sum queries, point updates, and range updates makes Lazy Propagation essential for handling complex range-based problems efficiently.
There are multiple ways to implement a Segment Tree with Lazy Propagation. A commonly used and efficient approach is the array-based representation, where the tree is stored in a flat array. This approach is both time-efficient and space-efficient.
If you want to understand why an array is used to represent a Segment Tree, you can refer to the explanation provided on CP-Algorithms.
The array-based representation for Lazy Propagation is the same as a normal Segment Tree. The only difference is the addition of a separatelazy array to store pending updates.
In this representation, the Segment Tree is stored in an array of size4 * n to safely accommodate all nodes.
0i is at 2 * i + 1i is at 2 * i + 2This representation allows efficient traversal and updates without using 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 or range updateqr → right boundary of the query or range updatepos → index of the element for a point updateval → value to be added during a point or range updateThe build function constructs the Segment Tree in the same way as a normal Segment Tree. Leaf nodes store individual array elements, and internal nodes store the merged result of their children. Thelazy array is initialized with zeros.
The push function is used to propagate pending lazy values from a parent node to its children. When a node has a non-zero lazy value and we need to access its children (during a query or further update), this value is pushed down to the child nodes.
During the push operation, a pending update stored at a node is propagated to its children. If a node representing the range [l, r] has a lazy valuelazy[idx] = x, then this value is passed down to both child nodes. The left child and right child each accumulate this value in their own lazy entries, and their segment values are updated accordingly.
For example, if a node covering the range[2, 5] has a pending update of+3, this update is not immediately applied to its children. When a push is required, the update is transferred to the children covering [2, 3] and[4, 5]. Each child stores the update in its lazy value, and the parent’s lazy value is then reset.
The push function ensures correctness by applying deferred updates at the right time, while still keeping all operations efficient withO(log n) complexity.
Range sum queries in a Lazy Segment Tree are conceptually similar to those in a normal Segment Tree, but with an important additional step. In a standard Segment Tree, we can directly use the stored node values because there are no pending updates.
In contrast, a Lazy Segment Tree may contain pending updates stored in thelazy array. Therefore, before using the value of any node, we must first check whether a lazy value exists. If a pending update is found, it is propagated to the child nodes using thepush function, and the current node’s value is updated accordingly.
After ensuring that all pending updates are applied, the query proceeds using the same overlap-based logic as a normal Segment Tree to compute the required range sum.
In a Lazy Segment Tree, a point update follows the same traversal path as in a normal Segment Tree, but with additional handling for pending updates. Starting from the root, the tree is traversed recursively toward the leaf node that represents the target index.
At each visited node, the algorithm first checks whether there is any pending update stored in the lazy array. If a lazy value exists, it is propagated downward using thepush function so that the current node and its children reflect all deferred updates before continuing the traversal.
Once the traversal reaches the leaf node corresponding to the update index, the node’s value is updated directly. As the recursion unwinds, all ancestor nodes are recomputed using the merge operation (such as summation), ensuring that the changes are correctly reflected throughout the tree.
The range update function is the core component of a Lazy Segment Tree. Its purpose is to apply an update operation (such as addition or assignment) to all elements within a given range[ql, qr] without explicitly updating every element in that range.
When the current segment[l, r] is completely outside the update range, the function returns immediately, as no update is required. If the segment is fully covered by the update range, the update is applied directly to the node’s stored value, and the update information is recorded in the lazy array. This indicates that the update should be propagated to the children at a later time.
In the case of partial overlap, the algorithm first checks for any pending lazy updates at the current node. If such updates exist, they are propagated downward using the push function to ensure correctness. The update operation is then recursively applied to both child segments, and after the recursion completes, the current node’s value is recomputed using the merge operation.
By deferring updates and propagating them only when necessary, the range update function guarantees that both update and query operations run inO(log n) time, even for large input sizes.
📌 Note: A point update is a special case of a range update where the update range consists of a single index. In other words, when the start and end of the update range are equal, both operations behave identically.
From an implementation perspective, this can be expressed as:
pointUpdate(idx, l, r, pos, val)
is equivalent torangeUpdate(idx, l, r, pos, pos, val)
This equivalence highlights that Lazy Propagation generalizes point updates into range updates, allowing both operations to be handled efficiently within a unified framework.
Here is a sample code snippet for Segment Tree with lazy propagation sum operations:
I have explained Segment Tree with Lazy Propagation based on my own understanding and what I learned while studying them. I hope this guide is helpful. If you find any mistakes or areas for improvement, please feel free to report them.
You can practice Lazy Propagation–based 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 with Lazy Propagation in different scenarios.
For a deeper understanding of 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 2These resources explain lazy propagation in detail and show how it is used to solve complex range update problems in competitive programming.