Open In App

Segment Tree for Range Multiplication and Sum Queries

Last Updated : 18 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Given an array of size N filled with all 1s, the task is to answer Q queries, where the queries can be one of the two types:
Type 1 (1, L, R, X): Multiply all elements on the segment from L to R−1 by X, and
Type 2 (2, L, R): Find the sum on the segment from L to R−1.

Examples:

Input: N = 5, Q = 6, queries[] = {
{1, 0, 3, 3},
{2, 1, 2},
{1, 1, 4, 4},
{2, 1, 3},
{2, 1, 4},
{2, 3, 5}}
Output: 3 24 28 5
Explanation:

  • Initially the array is {1, 1, 1, 1, 1}
  • First query is to multiply segment from index 0 to 2 by 3, so after first query the array is {3, 3, 3, 1, 1}.
  • Second query is to print the sum of segment from index 1 to 1, so the sum = 3.
  • Third query is to multiply segment from index 1 to 3 by 4, so after third query is {3, 12, 12, 4, 1, 1}.
  • Fourth query is to print the sum of segment from index 1 to 2, so the sum = 12 + 12 = 24.
  • Fifth query is to print the sum of segment from index 1 to 3, so the sum = 12 + 12 + 4 = 28.
  • Sixth query is to print the sum of segment from index 3 to 4, so the sum = 4 + 1 = 5.

Input: N = 2, Q = 2, queries[] = {
{1, 0, 1, 1000000},
{2 0 2}}
Output: 1000001

    Approach: To solve the problem, follow the below idea

    The idea is to use a segment tree data structure with lazy propagation to efficiently process the queries. The segment tree is built such that each node stores the sum of its corresponding segment of the array. When an update query is performed, instead of immediately updating all the elements in the range, the update is postponed and stored in a separate array called lazy. The pending updates are applied only when they are needed, i.e., when a sum query is performed on a node or its descendants.

    Step-by-step algorithm:

    • Declare arrays t and lazy for the segment tree and lazy propagation and initialize lazy values to 1.
    • Build the segment tree recursively using the build function.
    • Initialize lazy values to 1.
    • Lazy Propagation (Push):
      • Implement the push function to apply pending updates.
      • Update values based on the lazy value.
      • If the node is not a leaf, update the lazy values for the left and right children.
      • Reset the lazy value.
    • Update Range (Update):
      • Implement the update function to modify a range of the array and the segment tree.
      • Apply pending updates using push.
      • If the range is invalid, return.
      • If the range matches the segment, update the lazy value and apply the update immediately.
      • Otherwise, update the left and right children recursively and merge their values.
    • Query Range (Query):
      • Implement the query function to retrieve information about a range of the array.
      • Apply pending updates using push.
      • If the range is invalid, return 0.
      • If the range matches the segment, return the value of the segment.
      • Otherwise, calculate the middle and return the sum of queries on the left and right children.

    Below is the implementation of the algorithm:

    C++
    #include <bits/stdc++.h>;
    using namespace std;
    
    // Define the maximum size of the array and the modulo
    const int MAXN = 1e5 + 5;
    const int MOD = 1e9 + 7;
    
    // Initialize the array, segment tree, and lazy propagation
    // array
    int n, m;
    long long t[4 * MAXN], lazy[4 * MAXN];
    
    // Function to build the segment tree
    void build(int v, int tl, int tr)
    {
        // Initialize the lazy array with ones
        lazy[v] = 1;
        // If the segment has only one element
        if (tl == tr) {
            // Initialize the tree with the array
            // values
            t[v] = 1;
        }
        else {
            // Calculate the middle of the segment
            int tm = (tl + tr) / 2;
            // Recursively build the left child
            build(v * 2, tl, tm);
            // Recursively build the right child
            build(v * 2 + 1, tm + 1, tr);
            // Merge the children values
            t[v] = (t[v * 2] + t[v * 2 + 1]) % MOD;
        }
    }
    
    // Function to propagate the lazy values to the children
    void push(int v, int tl, int tr)
    {
        // Propagate the value
        t[v] = (t[v] * lazy[v]) % MOD;
        // If the node is not a leaf
        if (tl != tr) {
            // Update the lazy values for
            // the left child
            lazy[v * 2] = (lazy[v * 2] * lazy[v]) % MOD;
            // Update the lazy values
            // for the right child
            lazy[v * 2 + 1] = (lazy[v * 2 + 1] * lazy[v]) % MOD;
        }
        // Reset the lazy value
        lazy[v] = 1;
    }
    
    // Function to update a range of the array and the segment
    // tree
    void update(int v, int tl, int tr, int l, int r, int val)
    {
        // Apply the pending updates if any
        push(v, tl, tr);
        // If the range is invalid, return
        if (l > r)
            return;
        // If the range matches the segment
        if (l == tl && r == tr) {
            // Update the lazy value
            lazy[v] = (lazy[v] * val) % MOD;
            // Apply the update immediately
            push(v, tl, tr);
        }
        else {
            // Calculate the middle of the segment
            int tm = (tl + tr) / 2;
            // Recursively update the left child
            update(v * 2, tl, tm, l, min(r, tm), val);
            // Recursively update the right child
            update(v * 2 + 1, tm + 1, tr, max(l, tm + 1), r,
                   val);
            // Merge the children values
            t[v] = (t[v * 2] + t[v * 2 + 1]) % MOD;
        }
    }
    
    // Function to query a range of the array
    long long query(int v, int tl, int tr, int l, int r)
    {
        // Apply the pending updates if any
        push(v, tl, tr);
        // If the range is invalid, return 0
        if (l > r)
            return 0;
        // If the range matches the segment
        if (l <= tl && tr <= r) {
            // Return the value of the segment
            return t[v];
        }
        // Calculate the middle of the segment
        int tm = (tl + tr) / 2;
        // Return the sum of the queries on the
        // children
        return (query(v * 2, tl, tm, l, min(r, tm))
                + query(v * 2 + 1, tm + 1, tr, max(l, tm + 1),
                        r))
               % MOD;
    }
    
    int main()
    {
        ios_base::sync_with_stdio(false);
        cin.tie(0);
    
        // Input
        vector<vector<int>> arr
            = { { 1, 0, 3, 3 }, { 2, 1, 2 }, { 1, 1, 4, 4 },
                { 2, 1, 3 },    { 2, 1, 4 }, { 2, 3, 5 } };
        // Number of elements in the array
        n = 5;
        // Number of operations
        m = 6;
        // Build the segment tree
        build(1, 0, n - 1);
        for (int i = 0; i < m; i++) {
            // Type of the operation
            int type = arr[i][0];
            // If the operation is an update
            if (type == 1) {
                // Left boundary of the range
                int l = arr[i][1];
                // Right boundary of the range
                int r = arr[i][2];
                // Value to be multiplied
                int val = arr[i][3];
                // Update the range
                update(1, 0, n - 1, l, r - 1, val);
            }
            // If the operation is a query
            else {
                // Left boundary of the range
                int l = arr[i][1];
                // Right boundary of the range
                int r = arr[i][2];
                // Print the result of the query
                cout << query(1, 0, n - 1, l, r - 1) << "\n";
            }
        }
        return 0;
    }
    
    Java
    import java.util.Arrays;
    import java.util.Vector;
    
    class Main {
    
        static final int MAXN = 100005;
        static final int MOD = 1000000007;
        static long[] t = new long[4 * MAXN];
        static long[] lazy = new long[4 * MAXN];
        static int n, m;
    
        // Function to build the segment tree
        static void build(int v, int tl, int tr) {
            lazy[v] = 1;
            if (tl == tr) {
                t[v] = 1;
            } else {
                int tm = (tl + tr) / 2;
                build(v * 2, tl, tm);
                build(v * 2 + 1, tm + 1, tr);
                t[v] = (t[v * 2] + t[v * 2 + 1]) % MOD;
            }
        }
    
        // Function to propagate the lazy values to the children
        static void push(int v, int tl, int tr) {
            t[v] = (t[v] * lazy[v]) % MOD;
            if (tl != tr) {
                lazy[v * 2] = (lazy[v * 2] * lazy[v]) % MOD;
                lazy[v * 2 + 1] = (lazy[v * 2 + 1] * lazy[v]) % MOD;
            }
            lazy[v] = 1;
        }
    
        // Function to update a range of the array and the segment tree
        static void update(int v, int tl, int tr, int l, int r, int val) {
            push(v, tl, tr);
            if (l > r)
                return;
            if (l == tl && r == tr) {
                lazy[v] = (lazy[v] * val) % MOD;
                push(v, tl, tr);
            } else {
                int tm = (tl + tr) / 2;
                update(v * 2, tl, tm, l, Math.min(r, tm), val);
                update(v * 2 + 1, tm + 1, tr, Math.max(l, tm + 1), r, val);
                t[v] = (t[v * 2] + t[v * 2 + 1]) % MOD;
            }
        }
    
        // Function to query a range of the array
        static long query(int v, int tl, int tr, int l, int r) {
            push(v, tl, tr);
            if (l > r)
                return 0;
            if (l <= tl && tr <= r) {
                return t[v];
            }
            int tm = (tl + tr) / 2;
            return (query(v * 2, tl, tm, l, Math.min(r, tm))
                    + query(v * 2 + 1, tm + 1, tr, Math.max(l, tm + 1), r))
                   % MOD;
        }
    
        public static void main(String[] args) {
            n = 5;
            m = 6;
            Vector<Vector<Integer>> arr = new Vector<>(
                    Arrays.asList(new Vector<>(Arrays.asList(1, 0, 3, 3)),
                            new Vector<>(Arrays.asList(2, 1, 2)),
                            new Vector<>(Arrays.asList(1, 1, 4, 4)),
                            new Vector<>(Arrays.asList(2, 1, 3)),
                            new Vector<>(Arrays.asList(2, 1, 4)),
                            new Vector<>(Arrays.asList(2, 3, 5))));
            build(1, 0, n - 1);
    
            for (int i = 0; i < m; i++) {
                int type = arr.get(i).get(0);
                if (type == 1) {
                    int l = arr.get(i).get(1);
                    int r = arr.get(i).get(2);
                    int val = arr.get(i).get(3);
                    update(1, 0, n - 1, l, r - 1, val);
                } else {
                    int l = arr.get(i).get(1);
                    int r = arr.get(i).get(2);
                    System.out.println(query(1, 0, n - 1, l, r - 1));
                }
            }
        }
    }
    
    // This code is contributed by shivamgupta310570
    
    Python
    # Define the maximum size of the array and the modulo
    MAXN = 10**5 + 5
    MOD = 10**9 + 7
    
    # Initialize the array, segment tree, and lazy propagation array
    n, m = 0, 0
    t = [0] * (4 * MAXN)
    lazy = [1] * (4 * MAXN)
    
    # Function to build the segment tree
    def build(v, tl, tr):
        # If the segment has only one element
        if tl == tr:
            # Initialize the tree with the array values
            t[v] = 1
        else:
            # Calculate the middle of the segment
            tm = (tl + tr) // 2
            # Recursively build the left child
            build(v * 2, tl, tm)
            # Recursively build the right child
            build(v * 2 + 1, tm + 1, tr)
            # Merge the children values
            t[v] = (t[v * 2] + t[v * 2 + 1]) % MOD
    
    # Function to propagate the lazy values to the children
    def push(v, tl, tr):
        # Propagate the value
        t[v] = (t[v] * lazy[v]) % MOD
        # If the node is not a leaf
        if tl != tr:
            # Update the lazy values for the left child
            lazy[v * 2] = (lazy[v * 2] * lazy[v]) % MOD
            # Update the lazy values for the right child
            lazy[v * 2 + 1] = (lazy[v * 2 + 1] * lazy[v]) % MOD
        # Reset the lazy value
        lazy[v] = 1
    
    # Function to update a range of the array and the segment tree
    def update(v, tl, tr, l, r, val):
        # Apply the pending updates if any
        push(v, tl, tr)
        # If the range is invalid, return
        if l > r:
            return
        # If the range matches the segment
        if l == tl and r == tr:
            # Update the lazy value
            lazy[v] = (lazy[v] * val) % MOD
            # Apply the update immediately
            push(v, tl, tr)
        else:
            # Calculate the middle of the segment
            tm = (tl + tr) // 2
            # Recursively update the left child
            update(v * 2, tl, tm, l, min(r, tm), val)
            # Recursively update the right child
            update(v * 2 + 1, tm + 1, tr, max(l, tm + 1), r, val)
            # Merge the children values
            t[v] = (t[v * 2] + t[v * 2 + 1]) % MOD
    
    # Function to query a range of the array
    def query(v, tl, tr, l, r):
        # Apply the pending updates if any
        push(v, tl, tr)
        # If the range is invalid, return 0
        if l > r:
            return 0
        # If the range matches the segment
        if l <= tl and tr <= r:
            # Return the value of the segment
            return t[v]
        # Calculate the middle of the segment
        tm = (tl + tr) // 2
        # Return the sum of the queries on the children
        return (query(v * 2, tl, tm, l, min(r, tm)) + query(v * 2 + 1, tm + 1, tr, max(l, tm + 1), r)) % MOD
    
    # Input
    arr = [[1, 0, 3, 3], [2, 1, 2], [1, 1, 4, 4], [2, 1, 3], [2, 1, 4], [2, 3, 5]]
    # Number of elements in the array
    n = 5
    # Number of operations
    m = 6
    # Build the segment tree
    build(1, 0, n - 1)
    for i in range(m):
        # Type of the operation
        type = arr[i][0]
        # If the operation is an update
        if type == 1:
            # Left boundary of the range
            l = arr[i][1]
            # Right boundary of the range
            r = arr[i][2]
            # Value to be multiplied
            val = arr[i][3]
            # Update the range
            update(1, 0, n - 1, l, r - 1, val)
        # If the operation is a query
        else:
            # Left boundary of the range
            l = arr[i][1]
            # Right boundary of the range
            r = arr[i][2]
            # Print the result of the query
            print(query(1, 0, n - 1, l, r - 1))
    
    C#
    using System;
    using System.Collections.Generic;
    
    class MainClass
    {
        const int MAXN = 100005;
        const int MOD = 1000000007;
        static long[] t = new long[4 * MAXN];
        static long[] lazy = new long[4 * MAXN];
        static int n, m;
    
        // Function to build the segment tree
        static void Build(int v, int tl, int tr)
        {
            lazy[v] = 1;
            if (tl == tr)
            {
                t[v] = 1;
            }
            else
            {
                int tm = (tl + tr) / 2;
                Build(v * 2, tl, tm);
                Build(v * 2 + 1, tm + 1, tr);
                t[v] = (t[v * 2] + t[v * 2 + 1]) % MOD;
            }
        }
    
        // Function to propagate the lazy values to the children
        static void Push(int v, int tl, int tr)
        {
            t[v] = (t[v] * lazy[v]) % MOD;
            if (tl != tr)
            {
                lazy[v * 2] = (lazy[v * 2] * lazy[v]) % MOD;
                lazy[v * 2 + 1] = (lazy[v * 2 + 1] * lazy[v]) % MOD;
            }
            lazy[v] = 1;
        }
    
        // Function to update a range of the array and the segment tree
        static void Update(int v, int tl, int tr, int l, int r, int val)
        {
            Push(v, tl, tr);
            if (l > r)
                return;
            if (l == tl && r == tr)
            {
                lazy[v] = (lazy[v] * val) % MOD;
                Push(v, tl, tr);
            }
            else
            {
                int tm = (tl + tr) / 2;
                Update(v * 2, tl, tm, l, Math.Min(r, tm), val);
                Update(v * 2 + 1, tm + 1, tr, Math.Max(l, tm + 1), r, val);
                t[v] = (t[v * 2] + t[v * 2 + 1]) % MOD;
            }
        }
    
        // Function to query a range of the array
        static long Query(int v, int tl, int tr, int l, int r)
        {
            Push(v, tl, tr);
            if (l > r)
                return 0;
            if (l <= tl && tr <= r)
            {
                return t[v];
            }
            int tm = (tl + tr) / 2;
            return (Query(v * 2, tl, tm, l, Math.Min(r, tm))
                    + Query(v * 2 + 1, tm + 1, tr, Math.Max(l, tm + 1), r))
                   % MOD;
        }
    
        public static void Main(string[] args)
        {
            n = 5;
            m = 6;
            List<List<int>> arr = new List<List<int>>()
            {
                new List<int> { 1, 0, 3, 3 },
                new List<int> { 2, 1, 2 },
                new List<int> { 1, 1, 4, 4 },
                new List<int> { 2, 1, 3 },
                new List<int> { 2, 1, 4 },
                new List<int> { 2, 3, 5 }
            };
    
            Build(1, 0, n - 1);
    
            for (int i = 0; i < m; i++)
            {
                int type = arr[i][0];
                if (type == 1)
                {
                    int l = arr[i][1];
                    int r = arr[i][2];
                    int val = arr[i][3];
                    Update(1, 0, n - 1, l, r - 1, val);
                }
                else
                {
                    int l = arr[i][1];
                    int r = arr[i][2];
                    Console.WriteLine(Query(1, 0, n - 1, l, r - 1));
                }
            }
        }
    }
    
    JavaScript
    // Define the maximum size of the array and the modulo
    const MAXN = 1e5 + 5;
    const MOD = 1e9 + 7;
    
    // Initialize the array, segment tree, and lazy propagation
    let n, m;
    let t = new Array(4 * MAXN);
    let lazy = new Array(4 * MAXN);
    
    // Function to build the segment tree
    function build(v, tl, tr) {
        // Initialize the lazy array with ones
        lazy[v] = 1;
        // If the segment has only one element
        if (tl === tr) {
            // Initialize the tree with the array values
            t[v] = 1;
        } else {
            // Calculate the middle of the segment
            let tm = Math.floor((tl + tr) / 2);
            // Recursively build the left child
            build(v * 2, tl, tm);
            // Recursively build the right child
            build(v * 2 + 1, tm + 1, tr);
            // Merge the children values
            t[v] = (t[v * 2] + t[v * 2 + 1]) % MOD;
        }
    }
    
    // Function to propagate the lazy values to the children
    function push(v, tl, tr) {
        // Propagate the value
        t[v] = (t[v] * lazy[v]) % MOD;
        // If the node is not a leaf
        if (tl !== tr) {
            // Update the lazy values for the left child
            lazy[v * 2] = (lazy[v * 2] * lazy[v]) % MOD;
            // Update the lazy values for the right child
            lazy[v * 2 + 1] = (lazy[v * 2 + 1] * lazy[v]) % MOD;
        }
        // Reset the lazy value
        lazy[v] = 1;
    }
    
    // Function to update a range of the array and the segment tree
    function update(v, tl, tr, l, r, val) {
        // Apply the pending updates if any
        push(v, tl, tr);
        // If the range is invalid, return
        if (l > r)
            return;
        // If the range matches the segment
        if (l === tl && r === tr) {
            // Update the lazy value
            lazy[v] = (lazy[v] * val) % MOD;
            // Apply the update immediately
            push(v, tl, tr);
        } else {
            // Calculate the middle of the segment
            let tm = Math.floor((tl + tr) / 2);
            // Recursively update the left child
            update(v * 2, tl, tm, l, Math.min(r, tm), val);
            // Recursively update the right child
            update(v * 2 + 1, tm + 1, tr, Math.max(l, tm + 1), r, val);
            // Merge the children values
            t[v] = (t[v * 2] + t[v * 2 + 1]) % MOD;
        }
    }
    
    // Function to query a range of the array
    function query(v, tl, tr, l, r) {
        // Apply the pending updates if any
        push(v, tl, tr);
        // If the range is invalid, return 0
        if (l > r)
            return 0;
        // If the range matches the segment
        if (l <= tl && tr <= r) {
            // Return the value of the segment
            return t[v];
        }
        // Calculate the middle of the segment
        let tm = Math.floor((tl + tr) / 2);
        // Return the sum of the queries on the children
        return (query(v * 2, tl, tm, l, Math.min(r, tm))
                + query(v * 2 + 1, tm + 1, tr, Math.max(l, tm + 1), r))
               % MOD;
    }
    
    // Main function
    function main() {
        // Input
        let arr = [[1, 0, 3, 3], [2, 1, 2], [1, 1, 4, 4],
                   [2, 1, 3], [2, 1, 4], [2, 3, 5]];
        // Number of elements in the array
        n = 5;
        // Number of operations
        m = 6;
        // Build the segment tree
        build(1, 0, n - 1);
        for (let i = 0; i < m; i++) {
            // Type of the operation
            let type = arr[i][0];
            // If the operation is an update
            if (type === 1) {
                // Left boundary of the range
                let l = arr[i][1];
                // Right boundary of the range
                let r = arr[i][2];
                // Value to be multiplied
                let val = arr[i][3];
                // Update the range
                update(1, 0, n - 1, l, r - 1, val);
            }
            // If the operation is a query
            else {
                // Left boundary of the range
                let l = arr[i][1];
                // Right boundary of the range
                let r = arr[i][2];
                // Print the result of the query
                console.log(query(1, 0, n - 1, l, r - 1));
            }
        }
    }
    
    main();
    

    Output
    3
    24
    28
    5
    

    Time Complexity: O(NlogN + MlogN), where N is the array size and M is the number of operations.
    Auxiliary Space: O(N) for the segment tree and lazy propagation arrays.



    Like Article
    Suggest improvement
    Share your thoughts in the comments

    Similar Reads