Given an array of size N filled with all 0s, the task is to answer Q 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
#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;
}
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+ " ");
}
}
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)
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 + " ");
}
}
// 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