Open In App

Segment Tree for Range Multiplication and Sum Queries

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:

Below is the implementation of the algorithm:

#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;
}
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
# 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))
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));
            }
        }
    }
}
// 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.

Article Tags :