There is an array of n elements, initially filled with zeros. The task is to perform q queries from given queries[][] and there are two types of queries, type 1 queries in the format {1, l, r, v} and type 2 queries in the format {2, l, r}.
- Type 1: Apply the operation ai = ai | v (bitwise OR) to all elements on the segment l to r.
- Type 2: Find the minimum on the segment from l to r.
Examples:
Input: n = 5, q = 6, queries[6][4] = {{1, 0, 3, 3}, {2, 1, 2}, {1, 1, 4, 4}, {2, 1, 3}, {2, 1, 4}, {2, 3, 5} }; Output: 3 7 4 0
Input: n = 2, q = 3, queries[3][4] = {{1, 0, 1, 3}, {1, 1, 2, 9}, {2, 0, 2}}; Output: 1
Approach: To solve the problem follow the below idea
The solution uses a segment tree data structure. We’ll also uses lazy propagation to optimize the update operation. Lazy propagation is a strategy where, instead of updating the segment tree immediately after a query of type 1, the update is postponed and stored in a separate array called lazy. The lazy array keeps track of the pending updates for each node of the segment tree. The pending updates are applied only when they are needed, i.e., when a query of type 2 is performed on a node or its descendants.
Steps were taken to implement the idea:
Initialization:
- Define constants for the maximum size of the array (MAXN) and the maximum number of bits (MAXV).
- Declare variables for the array size (n), the number of operations (m), the array (a), and the segment tree (t).
Build Segment Tree:
- Create a function build to initialize the segment tree.
- If the segment has only one element, set the tree values based on the corresponding bits in the array.
- Otherwise, recursively build the left and right children, merging their values.
Lazy Propagation:
- Create a function push to propagate lazy values to the children of a segment.
- If the segment has more than one element, update the children based on the lazy values.
Update Operation:
- Create a function update to apply an update operation to a range of the array and the segment tree.
- Propagate lazy values, handle invalid ranges, and update the tree and lazy values based on the given range and value.
Query Operation:
- Create a function query to find the bitwise AND of elements in a range of the array.
- Propagate lazy values, handle invalid ranges, and return the bitwise AND of the segment.
Main Function:
- Read input for array size and the number of operations.
- Build the initial segment tree using the build function.
- Iterate through each operation:
- If it’s an update operation, apply the update using the update function.
- If it’s a query operation, print the result of the query using the query function.
Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
const int MAXV = 30;
int n, m, a[MAXN], t[MAXN << 2][MAXV],
lazy[MAXN << 2][MAXV];
void build( int v, int tl, int tr)
{
if (tl == tr) {
for ( int i = 0; i < MAXV; i++) {
t[v][i] = ((a[tl] >> i) & 1);
}
}
else {
int tm = (tl + tr) / 2;
build(v * 2, tl, tm );
build(v * 2 + 1, tm + 1, tr);
for ( int i = 0; i < MAXV; i++) {
t[v][i] = t[v * 2][i] & t[v * 2 + 1][i];
}
}
}
void push( int v, int tl, int tr)
{
if (tl != tr) {
for ( int i = 0; i < MAXV; i++) {
if (lazy[v][i]) {
t[v * 2][i] = t[v * 2 + 1][i]
= lazy[v * 2][i] = lazy[v * 2 + 1][i]
= 1;
lazy[v][i] = 0;
}
}
}
}
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) {
for ( int i = 0; i < MAXV; i++) {
if ((val >> i) & 1) {
t[v][i] = lazy[v][i] = 1;
}
}
}
else {
int tm = (tl + tr) / 2;
update(v * 2, tl, tm , l, min(r, tm ), val);
update(v * 2 + 1, tm + 1, tr, max(l, tm + 1), r,
val);
for ( int i = 0; i < MAXV; i++) {
t[v][i] = t[v * 2][i] & t[v * 2 + 1][i];
}
}
}
int query( int v, int tl, int tr, int l, int r)
{
push(v, tl, tr);
if (l > r)
return (1 << MAXV) - 1;
if (l <= tl && tr <= r) {
int res = 0;
for ( int i = 0; i < MAXV; i++) {
if (t[v][i]) {
res |= (1 << i);
}
}
return res;
}
int tm = (tl + tr) / 2;
return query(v * 2, tl, tm , l, min(r, tm ))
& query(v * 2 + 1, tm + 1, tr, max(l, tm + 1),
r);
}
int main()
{
n = 2;
m = 3;
int operations[3][4]
= { { 1, 0, 1, 3 }, { 1, 1, 2, 9 }, { 2, 0, 2 } };
build(1, 0, n - 1);
for ( int i = 0; i < m; i++) {
int type = operations[i][0];
if (type == 1) {
int l = operations[i][1];
int r = operations[i][2];
int val = operations[i][3];
update(1, 0, n - 1, l, r - 1, val);
}
else {
int l = operations[i][1];
int r = operations[i][2];
cout << query(1, 0, n - 1, l, r - 1) << "\n" ;
}
}
return 0;
}
|
Java
import java.util.*;
public class SegmentTreeLazyPropagation {
static final int MAXN = 100005 ;
static final int MAXV = 30 ;
static int n, m;
static int [] a = new int [MAXN];
static int [][] t = new int [MAXN << 2 ][MAXV];
static int [][] lazy = new int [MAXN << 2 ][MAXV];
static void build( int v, int tl, int tr) {
if (tl == tr) {
for ( int i = 0 ; i < MAXV; i++) {
t[v][i] = ((a[tl] >> i) & 1 );
}
} else {
int tm = (tl + tr) / 2 ;
build(v * 2 , tl, tm);
build(v * 2 + 1 , tm + 1 , tr);
for ( int i = 0 ; i < MAXV; i++) {
t[v][i] = t[v * 2 ][i] & t[v * 2 + 1 ][i];
}
}
}
static void push( int v, int tl, int tr) {
if (tl != tr) {
for ( int i = 0 ; i < MAXV; i++) {
if (lazy[v][i] != 0 ) {
t[v * 2 ][i] = t[v * 2 + 1 ][i] = lazy[v * 2 ][i] = lazy[v * 2 + 1 ][i] = 1 ;
lazy[v][i] = 0 ;
}
}
}
}
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) {
for ( int i = 0 ; i < MAXV; i++) {
if (((val >> i) & 1 ) == 1 ) {
t[v][i] = lazy[v][i] = 1 ;
}
}
} 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);
for ( int i = 0 ; i < MAXV; i++) {
t[v][i] = t[v * 2 ][i] & t[v * 2 + 1 ][i];
}
}
}
static int query( int v, int tl, int tr, int l, int r) {
push(v, tl, tr);
if (l > r) return ( 1 << MAXV) - 1 ;
if (l <= tl && tr <= r) {
int res = 0 ;
for ( int i = 0 ; i < MAXV; i++) {
if (t[v][i] == 1 ) {
res |= ( 1 << i);
}
}
return res;
}
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);
}
public static void main(String[] args) {
n = 2 ;
m = 3 ;
int [][] operations = { { 1 , 0 , 1 , 3 }, { 1 , 1 , 2 , 9 }, { 2 , 0 , 2 } };
build( 1 , 0 , n - 1 );
for ( int i = 0 ; i < m; i++) {
int type = operations[i][ 0 ];
if (type == 1 ) {
int l = operations[i][ 1 ];
int r = operations[i][ 2 ];
int val = operations[i][ 3 ];
update( 1 , 0 , n - 1 , l, r - 1 , val);
} else {
int l = operations[i][ 1 ];
int r = operations[i][ 2 ];
System.out.println(query( 1 , 0 , n - 1 , l, r - 1 ));
}
}
}
}
|
C#
using System;
public class MainClass
{
const int MAXN = 100005;
const int MAXV = 30;
static int n, m;
static int [,] a = new int [MAXN, MAXV];
static int [,] t = new int [MAXN << 2, MAXV];
static int [] lazy = new int [MAXN << 2];
static void Build( int v, int tl, int tr)
{
if (tl == tr)
{
for ( int i = 0; i < MAXV; i++)
{
t[v, i] = ((a[tl, i] >> i) & 1);
}
}
else
{
int tm = (tl + tr) / 2;
Build(v * 2, tl, tm);
Build(v * 2 + 1, tm + 1, tr);
for ( int i = 0; i < MAXV; i++)
{
t[v, i] = t[v * 2, i] & t[v * 2 + 1, i];
}
}
}
static void Push( int v, int tl, int tr)
{
if (tl != tr)
{
for ( int i = 0; i < MAXV; i++)
{
if (lazy[v] != 0)
{
t[v * 2, i] = t[v * 2 + 1, i] = lazy[v * 2] = lazy[v * 2 + 1] = 1;
lazy[v] = 0;
}
}
}
}
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)
{
for ( int i = 0; i < MAXV; i++)
{
if (((val >> i) & 1) != 0)
{
t[v, i] = lazy[v] = 1;
}
}
}
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);
for ( int i = 0; i < MAXV; i++)
{
t[v, i] = t[v * 2, i] & t[v * 2 + 1, i];
}
}
}
static int Query( int v, int tl, int tr, int l, int r)
{
Push(v, tl, tr);
if (l > r)
return (1 << MAXV) - 1;
if (l <= tl && tr <= r)
{
int res = 0;
for ( int i = 0; i < MAXV; i++)
{
if (t[v, i] != 0)
{
res |= (1 << i);
}
}
return res;
}
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);
}
public static void Main( string [] args)
{
n = 2;
m = 3;
int [,] operations = new int [3, 4]
{
{ 1, 0, 1, 3 },
{ 1, 1, 2, 9 },
{ 2, 0, 2, 0 }
};
Build(1, 0, n - 1);
for ( int i = 0; i < m; i++)
{
int type = operations[i, 0];
if (type == 1)
{
int l = operations[i, 1];
int r = operations[i, 2];
int val = operations[i, 3];
Update(1, 0, n - 1, l, r - 1, val);
}
else
{
int l = operations[i, 1];
int r = operations[i, 2];
Console.WriteLine(Query(1, 0, n - 1, l, r - 1));
}
}
}
}
|
Javascript
const MAXN = 100005;
const MAXV = 30;
let n = 2, m = 3;
let a = Array(MAXN).fill(0);
let t = Array.from(Array(MAXN << 2), () => Array(MAXV).fill(0));
let lazy = Array.from(Array(MAXN << 2), () => Array(MAXV).fill(0));
function build(v, tl, tr) {
if (tl === tr) {
for (let i = 0; i < MAXV; i++) {
t[v][i] = ((a[tl] >> i) & 1);
}
} else {
let tm = Math.floor((tl + tr) / 2);
build(v * 2, tl, tm);
build(v * 2 + 1, tm + 1, tr);
for (let i = 0; i < MAXV; i++) {
t[v][i] = t[v * 2][i] & t[v * 2 + 1][i];
}
}
}
function push(v, tl, tr) {
if (tl !== tr) {
for (let i = 0; i < MAXV; i++) {
if (lazy[v][i]) {
t[v * 2][i] = t[v * 2 + 1][i] = lazy[v * 2][i] = lazy[v * 2 + 1][i] = 1;
lazy[v][i] = 0;
}
}
}
}
function update(v, tl, tr, l, r, val) {
push(v, tl, tr);
if (l > r)
return ;
if (l === tl && r === tr) {
for (let i = 0; i < MAXV; i++) {
if ((val >> i) & 1) {
t[v][i] = lazy[v][i] = 1;
}
}
} else {
let tm = Math.floor((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);
for (let i = 0; i < MAXV; i++) {
t[v][i] = t[v * 2][i] & t[v * 2 + 1][i];
}
}
}
function query(v, tl, tr, l, r) {
push(v, tl, tr);
if (l > r)
return (1 << MAXV) - 1;
if (l <= tl && tr <= r) {
let res = 0;
for (let i = 0; i < MAXV; i++) {
if (t[v][i]) {
res |= (1 << i);
}
}
return res;
}
let tm = Math.floor((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);
}
function main() {
let operations = [[1, 0, 1, 3], [1, 1, 2, 9], [2, 0, 2]];
build(1, 0, n - 1);
for (let i = 0; i < m; i++) {
let type = operations[i][0];
if (type === 1) {
let l = operations[i][1];
let r = operations[i][2];
let val = operations[i][3];
update(1, 0, n - 1, l, r - 1, val);
} else {
let l = operations[i][1];
let r = operations[i][2];
console.log(query(1, 0, n - 1, l, r - 1));
}
}
}
main();
|
Python3
class SegmentTree:
def __init__( self , n):
self .n = n
self .MAXV = 30
self .t = [[ 0 ] * self .MAXV for _ in range (n * 4 )]
self .lazy = [[ 0 ] * self .MAXV for _ in range (n * 4 )]
def build( self , v, tl, tr, a):
if tl = = tr:
for i in range ( self .MAXV):
self .t[v][i] = ((a[tl] >> i) & 1 )
else :
tm = (tl + tr) / / 2
self .build(v * 2 , tl, tm, a)
self .build(v * 2 + 1 , tm + 1 , tr, a)
for i in range ( self .MAXV):
self .t[v][i] = self .t[v * 2 ][i] & self .t[v * 2 + 1 ][i]
def push( self , v, tl, tr):
if tl ! = tr:
for i in range ( self .MAXV):
if self .lazy[v][i]:
self .t[v * 2 ][i] = self .t[v * 2 + 1 ][i] = self .lazy[v * 2 ][i] = self .lazy[v * 2 + 1 ][i] = 1
self .lazy[v][i] = 0
def update( self , v, tl, tr, l, r, val):
self .push(v, tl, tr)
if l > r:
return
if l = = tl and r = = tr:
for i in range ( self .MAXV):
if (val >> i) & 1 :
self .t[v][i] = self .lazy[v][i] = 1
else :
tm = (tl + tr) / / 2
self .update(v * 2 , tl, tm, l, min (r, tm), val)
self .update(v * 2 + 1 , tm + 1 , tr, max (l, tm + 1 ), r, val)
for i in range ( self .MAXV):
self .t[v][i] = self .t[v * 2 ][i] & self .t[v * 2 + 1 ][i]
def query( self , v, tl, tr, l, r):
self .push(v, tl, tr)
if l > r:
return ( 1 << self .MAXV) - 1
if l < = tl and tr < = r:
res = 0
for i in range ( self .MAXV):
if self .t[v][i]:
res | = ( 1 << i)
return res
tm = (tl + tr) / / 2
return self .query(v * 2 , tl, tm, l, min (r, tm)) & self .query(v * 2 + 1 , tm + 1 , tr, max (l, tm + 1 ), r)
n = 2
m = 3
a = [ 0 , 0 ]
operations = [[ 1 , 0 , 1 , 3 ], [ 1 , 1 , 2 , 9 ], [ 2 , 0 , 2 ]]
segment_tree = SegmentTree(n)
segment_tree.build( 1 , 0 , n - 1 , a)
for operation in operations:
type_op = operation[ 0 ]
if type_op = = 1 :
l, r, val = operation[ 1 ], operation[ 2 ], operation[ 3 ]
segment_tree.update( 1 , 0 , n - 1 , l, r - 1 , val)
else :
l, r = operation[ 1 ], operation[ 2 ]
print (segment_tree.query( 1 , 0 , n - 1 , l, r - 1 ))
|
Time Complexity: O((n + m) * log(n) * MAXV)
- Build Operation: O(n * MAXV * log(n))
- Update Operation: O(log(n) * MAXV)
- Query Operation: O(log(n) * MAXV)
- Overall: O((n + m) * log(n) * MAXV)
Auxiliary Space: O(n * MAXV)
- Segment Tree: O(n * MAXV)
- Lazy Propagation Array: O(n * MAXV)
- Other Variables: O(1)
|