Segment Tree for Range Assignment and Range Sum Queries

Last Updated : 2 Jan, 2026

Given an array of size filled with all 0s, the task is to answer queries, where the queries can be one of the two types:

  • Type 1 (1, L, R, X): Assign value X to all elements on the segment from L to R, and
  • Type 2 (2, L, R): Find the sum on the segment from L to R.

Examples:

Input: N = 5, Q = 3, queries[][] = [[1, 0, 2, 5], [2, 1, 3], [1, 2, 3, 10]]
Output: 10
Explanation:
Initially, the array is {0, 0, 0, 0, 0}
The first query assigns value 5 to indices 0 through 2, so the array becomes {5, 5, 5, 0, 0}
The second query asks for the sum from indices 1 through 3, which is 5 + 5 + 0 = 10
The third query assigns value 10 to indices 2 through 3, so the array becomes {5, 5, 10, 10, 0}

Input: N = 10, Q = 4, queries[q][4] = [[1, 0, 4, 3], [2, 2, 6], [1, 4, 8, 2], [2, 0, 8]]
Output: [9, 22]
Explanation:
Initially, the array is {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
After assigning value 3 to indices 0 through 4, the array becomes {3, 3, 3, 3, 3, 0, 0, 0, 0, 0}
The second query computes the sum from indices 2 through 6, which is 3 + 3 + 3 + 0 + 0 = 9
Next, assigning value 2 to indices 4 through 8, the array becomes {3, 3, 3, 3, 2, 2, 2, 2, 2, 0}
The final query asks for the sum from indices 0 through 8, which is 3 + 3 + 3 + 3 + 2 + 2 + 2 + 2 + 2 = 22

[Approach] Using Segment Tree with Lazy Propagation - O(Q log(N)) Time and O(N) Space

We use a segment tree with lazy propagation. The segment tree stores the sum of segments of the array, allowing range sum queries to be answered in O(log N) time. Each node of the tree represents a segment, and its value is the sum of all elements in that segment.

Lazy propagation is used to efficiently support range assignment updates. When an update completely covers a segment, instead of immediately updating all its children, we store the assignment in a lazy array. This deferred update is applied only when that segment is accessed again, either during another update or a query. This avoids unnecessary work and keeps the operations efficient.

During a range update, if the current segment lies entirely within the update range, we directly update the node and mark its children as lazy. If the segment partially overlaps, we push any pending updates and recursively update both children, then recompute the current node’s sum.

For range sum queries, any pending lazy updates are applied before accessing the node. If the segment is fully inside the query range, its stored sum is returned. Otherwise, the query is propagated to the left and right children, and their results are added

C++
#include <iostream>
#include <vector>
using namespace std;

// Segment Tree class with lazy propagation
class SegmentTree {
    vector<long long> tree, lazy;
    int n;

public:
    // Constructor to initialize segment tree and lazy arrays
    SegmentTree(int size) {
        n = size;
        tree.assign(4 * n, 0);
        
         // LLONG_MIN indicates no pending assignment
        lazy.assign(4 * n, LLONG_MIN);
    }

    // Push pending updates to children
    void push(int node, int start, int end) {
        if (lazy[node] != LLONG_MIN) {
         
            // Apply lazy value to current node
            tree[node] = (long long)(end - start + 1) * lazy[node];
          
            // Propagate lazy value to children if not a leaf
            if (start != end) {
                lazy[node * 2] = lazy[node];
                lazy[node * 2 + 1] = lazy[node];
            }
            
             // Clear lazy value
            lazy[node] = LLONG_MIN;
        }
    }

    // Update values in range [l,r] to val
    void updateRange(int node, int start, int end, int l, int r, long long val) {
        push(node, start, end);

        // No overlap
        if (r < start || end < l) return;

        // Total overlap
        if (l <= start && end <= r) {
            lazy[node] = val;
            push(node, start, end);
            return;
        }

        // Partial overlap, recurse on children
        int mid = (start + end) / 2;
        updateRange(node * 2, start, mid, l, r, val);
        updateRange(node * 2 + 1, mid + 1, end, l, r, val);

        // Update current node based on children
        tree[node] = tree[node * 2] + tree[node * 2 + 1];
    }

    // Query sum of values in range [l,r]
    long long queryRange(int node, int start, int end, int l, int r) {
        push(node, start, end);

        // No overlap
        if (r < start || end < l) return 0;

        // Total overlap
        if (l <= start && end <= r) return tree[node];

        // Partial overlap, query children
        int mid = (start + end) / 2;
        return queryRange(node * 2, start, mid, l, r) +
               queryRange(node * 2 + 1, mid + 1, end, l, r);
    }
};

// Function to handle all queries and return type 2 query results
vector<long long> handleQueries(int N, vector<vector<long long>> &queries) {
    
    // Create segment tree of size N
    SegmentTree seg(N);  
    
    // Store results of sum queries
    vector<long long> results; 

    for (auto &q : queries) {
        int type = q[0];
        int l = q[1];
        int r = q[2];
        
        // Assignment query
        if (type == 1) { 
            long long val = q[3];
            
            // Update range [l,r] to val
            seg.updateRange(1, 1, N, l, r, val); 
        } else { 
        
            // Query sum in [l,r]
            results.push_back(seg.queryRange(1, 1, N, l, r)); 
        }
    }

    return results;
}

int main() {
    int N = 5;
    vector<vector<long long>> queries = {
        {1, 1, 3, 3},
        {2, 2, 2},   
        {1, 2, 4, 4},
        {2, 2, 3}     
    };

    vector<long long> res = handleQueries(N, queries);

    for (auto x : res) cout << x << " ";

    return 0;
}
Java
import java.util.*;

// Segment Tree class with lazy propagation
class SegmentTree {
    long[] tree, lazy;
    int n;

    // Constructor to initialize segment tree and lazy arrays
    SegmentTree(int size) {
        n = size;
        tree = new long[4 * n];

        // Use Long.MIN_VALUE to indicate no pending assignment
        lazy = new long[4 * n];
        Arrays.fill(lazy, Long.MIN_VALUE);
    }

    // Push pending updates to children
    void push(int node, int start, int end) {
        if (lazy[node] != Long.MIN_VALUE) {

            // Apply lazy value to current node
            tree[node] = (end - start + 1L) * lazy[node];

            // Propagate lazy value to children if not a leaf
            if (start != end) {
                lazy[node * 2] = lazy[node];
                lazy[node * 2 + 1] = lazy[node];
            }

            // Clear lazy value
            lazy[node] = Long.MIN_VALUE;
        }
    }

    // Update values in range [l,r] to val
    void updateRange(int node, int start, int end, int l, int r, long val) {
        push(node, start, end);

        // No overlap
        if (r < start || end < l) return;

        // Total overlap
        if (l <= start && end <= r) {
            lazy[node] = val;
            push(node, start, end);
            return;
        }

        // Partial overlap, recurse on children
        int mid = (start + end) / 2;
        updateRange(node * 2, start, mid, l, r, val);
        updateRange(node * 2 + 1, mid + 1, end, l, r, val);

        // Update current node based on children
        tree[node] = tree[node * 2] + tree[node * 2 + 1];
    }

    // Query sum of values in range [l,r]
    long queryRange(int node, int start, int end, int l, int r) {
        push(node, start, end);

        // No overlap
        if (r < start || end < l) return 0;

        // Total overlap
        if (l <= start && end <= r) return tree[node];

        // Partial overlap, query children
        int mid = (start + end) / 2;
        return queryRange(node * 2, start, mid, l, r) +
               queryRange(node * 2 + 1, mid + 1, end, l, r);
    }
}

// Function to handle all queries and return type 2 query results
class Solution {
    public static List<Long> handleQueries(int N, int[][] queries) {

        // Create segment tree of size N
        SegmentTree seg = new SegmentTree(N);

        // Store results of sum queries
        List<Long> results = new ArrayList<>();

        for (int i = 0; i < queries.length; i++) {
            int type = queries[i][0];
            int l = queries[i][1];
            int r = queries[i][2];

            // Assignment query
            if (type == 1) {
                long val = queries[i][3];

                // Update range [l,r] to val
                seg.updateRange(1, 1, N, l, r, val);
            } else {

                // Query sum in [l,r]
                results.add(seg.queryRange(1, 1, N, l, r));
            }
        }

        return results;
    }

    public static void main(String[] args) {
        int N = 5;
        int[][] queries = {
            {1,1,3,3},
            {2,2,2},
            {1,2,4,4},
            {2,2,3}
        };

        List<Long> res = handleQueries(N, queries);

        for (long x : res) System.out.print(x+ " ");
    }
}
Python
class SegmentTree:
    def __init__(self, size):
        self.n = size

        # tree array for segment sums
        self.tree = [0] * (4 * self.n)

        # lazy array for pending assignments
        self.lazy = [None] * (4 * self.n)

    def push(self, node, start, end):

        # apply pending assignment
        if self.lazy[node] is not None:
            self.tree[node] = (end - start + 1) * self.lazy[node]

            # propagate to children
            if start != end:
                self.lazy[node*2] = self.lazy[node]
                self.lazy[node*2+1] = self.lazy[node]

            # clear lazy
            self.lazy[node] = None

    def updateRange(self, node, start, end, l, r, val):

        # handle pending updates
        self.push(node, start, end)

        # no overlap
        if r < start or end < l:
            return

        # total overlap
        if l <= start and end <= r:
            self.lazy[node] = val
            self.push(node, start, end)
            return

        # partial overlap
        mid = (start + end) // 2
        self.updateRange(node*2, start, mid, l, r, val)
        self.updateRange(node*2+1, mid+1, end, l, r, val)

        # update current node
        self.tree[node] = self.tree[node*2] + self.tree[node*2+1]

    def queryRange(self, node, start, end, l, r):

        # handle pending updates
        self.push(node, start, end)

        # no overlap
        if r < start or end < l:
            return 0

        # total overlap
        if l <= start and end <= r:
            return self.tree[node]

        # partial overlap
        mid = (start + end) // 2
        return self.queryRange(node*2, start, mid, l, r) + \
               self.queryRange(node*2+1, mid+1, end, l, r)

def handleQueries(N, queries):

    # initialize segment tree
    seg = SegmentTree(N)

    # store sum query results
    results = []

    for q in queries:
        type = q[0]
        l = q[1]
        r = q[2]

        if type == 1:

            # assignment query
            val = q[3]
            seg.updateRange(1, 1, N, l, r, val)

        else:

            # sum query
            results.append(seg.queryRange(1, 1, N, l, r))

    return results

if __name__ == "__main__":
    N = 5
    queries = [
        [1, 1, 3, 3],
        [2, 2, 2],
        [1, 2, 4, 4],
        [2, 2, 3]
    ]

    res = handleQueries(N, queries)
    print(*res)
C#
using System;
using System.Collections.Generic;

// Segment Tree class with lazy propagation
class SegmentTree {
    long[] tree, lazy;
    int n;

    // Constructor to initialize segment tree and lazy arrays
    public SegmentTree(int size) {
        n = size;
        tree = new long[4 * n];

        // Use long.MinValue to indicate no pending assignment
        lazy = new long[4 * n];
        for (int i = 0; i < 4 * n; i++) lazy[i] = long.MinValue;
    }

    // Push pending updates to children
    void Push(int node, int start, int end) {
        if (lazy[node] != long.MinValue) {

            // Apply lazy value to current node
            tree[node] = (end - start + 1) * lazy[node];

            // Propagate lazy value to children if not a leaf
            if (start != end) {
                lazy[node * 2] = lazy[node];
                lazy[node * 2 + 1] = lazy[node];
            }

            // Clear lazy value
            lazy[node] = long.MinValue;
        }
    }

    // Update values in range [l,r] to val
    public void UpdateRange(int node, int start, int end, int l, int r, long val) {
        Push(node, start, end);

        // No overlap
        if (r < start || end < l) return;

        // Total overlap
        if (l <= start && end <= r) {
            lazy[node] = val;
            Push(node, start, end);
            return;
        }

        // Partial overlap, recurse on children
        int mid = (start + end) / 2;
        UpdateRange(node * 2, start, mid, l, r, val);
        UpdateRange(node * 2 + 1, mid + 1, end, l, r, val);

        // Update current node based on children
        tree[node] = tree[node * 2] + tree[node * 2 + 1];
    }

    // Query sum of values in range [l,r]
    public long QueryRange(int node, int start, int end, int l, int r) {
        Push(node, start, end);

        // No overlap
        if (r < start || end < l) return 0;

        // Total overlap
        if (l <= start && end <= r) return tree[node];

        // Partial overlap, query children
        int mid = (start + end) / 2;
        return QueryRange(node * 2, start, mid, l, r) +
               QueryRange(node * 2 + 1, mid + 1, end, l, r);
    }
}

// Function to handle all queries and return type 2 query results
class GFG {
    public static List<long> HandleQueries(int N, int[,] queries) {

        // Create segment tree of size N
        SegmentTree seg = new SegmentTree(N);

        // Store results of sum queries
        List<long> results = new List<long>();

        int qCount = queries.GetLength(0);

        for (int i = 0; i < qCount; i++) {
            int type = queries[i, 0];
            int l = queries[i, 1];
            int r = queries[i, 2];

            // Assignment query
            if (type == 1) {
                long val = queries[i, 3];

                // Update range [l,r] to val
                seg.UpdateRange(1, 1, N, l, r, val);
            } else {

                // Query sum in [l,r]
                results.Add(seg.QueryRange(1, 1, N, l, r));
            }
        }

        return results;
    }

    static void Main() {
        int N = 5;
        int[,] queries = new int[,] {
            {1, 1, 3, 3},
            {2, 2, 2, 0}, 
            {1, 2, 4, 4},
            {2, 2, 3, 0}  
        };


        List<long> res = HandleQueries(N, queries);

        foreach (var x in res) Console.Write(x + " ");
    }
}
JavaScript
// Segment Tree class with lazy propagation
class SegmentTree {
    constructor(size) {
        this.n = size;
        this.tree = Array(4 * size).fill(0);

        // Use null to indicate no pending assignment
        this.lazy = Array(4 * size).fill(null);
    }

    // Push pending updates to children
    push(node, start, end) {
        if (this.lazy[node] !== null) {
            
            // Apply lazy value to current node
            this.tree[node] = (end - start + 1) * this.lazy[node];

            // Propagate lazy value to children if not a leaf
            if (start !== end) {
                this.lazy[node * 2] = this.lazy[node];
                this.lazy[node * 2 + 1] = this.lazy[node];
            }

            // Clear lazy value
            this.lazy[node] = null;
        }
    }

    // Update values in range [l,r] to val
    updateRange(node, start, end, l, r, val) {
        this.push(node, start, end);

        // No overlap
        if (r < start || end < l) return;

        // Total overlap
        if (l <= start && end <= r) {
            this.lazy[node] = val;
            this.push(node, start, end);
            return;
        }

        // Partial overlap, recurse on children
        let mid = Math.floor((start + end) / 2);
        this.updateRange(node * 2, start, mid, l, r, val);
        this.updateRange(node * 2 + 1, mid + 1, end, l, r, val);

        // Update current node based on children
        this.tree[node] = this.tree[node * 2] + this.tree[node * 2 + 1];
    }

    // Query sum of values in range [l,r]
    queryRange(node, start, end, l, r) {
        this.push(node, start, end);

        // No overlap
        if (r < start || end < l) return 0;

        // Total overlap
        if (l <= start && end <= r) return this.tree[node];

        // Partial overlap, query children
        let mid = Math.floor((start + end) / 2);
        return this.queryRange(node * 2, start, mid, l, r) +
               this.queryRange(node * 2 + 1, mid + 1, end, l, r);
    }
}

// Function to handle all queries and return type 2 query results
function handleQueries(N, queries) {
    
    // Create segment tree of size N
    let seg = new SegmentTree(N);

    // Store results of sum queries
    let results = [];

    for (let q of queries) {
        let type = q[0], l = q[1], r = q[2];

        // Assignment query
        if (type === 1) {
            let val = q[3];

            // Update range [l,r] to val
            seg.updateRange(1, 1, N, l, r, val);
        } else {

            // Query sum in [l,r]
            results.push(seg.queryRange(1, 1, N, l, r));
        }
    }

    return results;
}

// Driver Code
let N = 5;
let queries = [
    [1,1,3,3],
    [2,2,2],
    [1,2,4,4],
    [2,2,3]
];

let res = handleQueries(N, queries);
console.log(...res);

Output
3 8 


Comment