Skip to content
Related Articles

Related Articles

Range Update Queries to XOR with 1 in a Binary Array.
  • Last Updated : 07 Jan, 2021

Given a binary array arr[] of size N. The task is to answer Q queries which can be of any one type from below: 
Type 1 – 1 l r : Performs bitwise xor operation on all the array elements from l to r with 1. 
Type 2 – 2 l r : Returns the minimum distance between two elements with value 1 in a subarray [l, r]. 
Type 3 – 3 l r : Returns the maximum distance between two elements with value 1 in a subarray [l, r]. 
Type 4 – 4 l r : Returns the minimum distance between two elements with value 0 in a subarray [l, r]. 
Type 5 – 5 l r : Returns the maximum distance between two elements with value 0 in a subarray [l, r].
Examples: 
 

Input : arr[] = {1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0}, q=5 
Output : 2 2 3 2
Explanation :
query 1 : Type 2, l=3, r=7 
Range 3 to 7 contains { 1, 0, 1, 0, 1 }. 
So, the minimum distance between two elements with value 1 is 2.
query 2 : Type 3, l=2, r=5 
Range 2 to 5 contains { 0, 1, 0, 1 }. 
So, the maximum distance between two elements with value 1 is 2.
query 3 : Type 1, l=1, r=4 
After update array becomes {1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0}
query 4 : Type 4, l=3, r=7 
Range 3 to 7 in updated array contains { 0, 1, 1, 0, 1 }. 
So, the minimum distance between two elements with value 0 is 3.
query 5 : Type 5, l=4, r=9 
Range 4 to 9 contains { 1, 1, 0, 1, 0, 1 }. 
So, the maximum distance between two elements with value 0 is 2.
 

 

Approach: 
We will create a segment tree and use range updates with lazy propagation to solve this.
 

  1. Each node in the segment tree will have the index of leftmost 1 as well as rightmost 1, leftmost 0 as well as rightmost 0 and integers containing the maximum and minimum distance between any elements with value 1 in a subarray {l, r} as well as the maximum and minimum distance between any elements with value 0 in a subarray {l, r}. 
     
  2. Now, in this segment tree we can merge left and right nodes as below: 
     

CPP



filter_none

edit
close

play_arrow

link
brightness_4
code

// l1 = leftmost index of 1, l0 = leftmost index of 0.
// r1 = rightmost index of 1, r0 = rightmost index of 0.
// max1 = maximum distance between two 1’s.
// max0 = maximum distance between two 0’s.
// min1 = minimum distance between two 1’s.
// min0 = minimum distance between two 0’s.
node Merge(node left, node right)
{
    node cur;
     
    if left.l0 is valid
        cur.l0 = left.l0
    else
        cur.l0 = r.l0
    // We will do this for all values
    // i.e. cur.r0, cur.l1, cur.r1, cur.l0
     
    // To find the min and max difference between two 1's and 0's
    // we will take min/max value of left side, right side and
    // difference between rightmost index of 1/0 in right node
    // and leftmost index of 1/0 in left node respectively.
         
     cur.min0 = minimum of left.min0 and right.min0
  
     if left.r0 is valid and right.l0 is valid
        cur.min0 = minimum of cur.min0 and (right.l0 - left.r0)
    // We will do this for all max/min values
    // i.e. cur.min0, cur.min1, cur.max1, cur.max0
         
    return cur;
}

chevron_right


  1.  
  2. To handle the range update query, we will use lazy propagation. The update query asks us to xor all the elements in the range from l to r with 1, and from observations, we know that : 
     
       0 xor 1 = 1
       1 xor 1 = 0
  1. Hence, we can observe that after this update all the 0’s will change to 1 and all the 1’s will change to 0. Thus, in our segment tree nodes, all the corresponding values for 0 and 1 will also get swapped i.e. 
     
       l0 and l1 will get swapped
       r0 and r1 will get swapped
       min0 and min1 will get swapped
       max0 and max1 will get swapped
  1.  
  2. Then, finally to find the answer to tasks 2, 3, 4 and 5 we just need to call query function for the given range {l, r} and i order to find the answer to task 1 we need to call the range update function.

Below is the implementation of the above approach:
 

CPP

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ program for the given problem
#include <bits/stdc++.h>
using namespace std;
 
int lazy[100001];
 
// Class for each node
// in the segment tree
class node {
public:
    int l1, r1, l0, r0;
    int min0, max0, min1, max1;
 
    node()
    {
        l1 = r1 = l0 = r0 = -1;
 
        max1 = max0 = INT_MIN;
        min1 = min0 = INT_MAX;
    }
 
} seg[100001];
 
// A utility function for
// merging two nodes
node MergeUtil(node l, node r)
{
    node x;
 
    x.l0 = (l.l0 != -1) ? l.l0 : r.l0;
    x.r0 = (r.r0 != -1) ? r.r0 : l.r0;
 
    x.l1 = (l.l1 != -1) ? l.l1 : r.l1;
    x.r1 = (r.r1 != -1) ? r.r1 : l.r1;
 
    x.min0 = min(l.min0, r.min0);
    if (l.r0 != -1 && r.l0 != -1)
        x.min0 = min(x.min0, r.l0 - l.r0);
 
    x.min1 = min(l.min1, r.min1);
    if (l.r1 != -1 && r.l1 != -1)
        x.min1 = min(x.min1, r.l1 - l.r1);
 
    x.max0 = max(l.max0, r.max0);
    if (l.l0 != -1 && r.r0 != -1)
        x.max0 = max(x.max0, r.r0 - l.l0);
 
    x.max1 = max(l.max1, r.max1);
    if (l.l1 != -1 && r.r1 != -1)
        x.max1 = max(x.max1, r.r1 - l.l1);
 
    return x;
}
 
// utility function
// for updating a node
node UpdateUtil(node x)
{
    swap(x.l0, x.l1);
    swap(x.r0, x.r1);
    swap(x.min1, x.min0);
    swap(x.max0, x.max1);
 
    return x;
}
 
// A recursive function that constructs
// Segment Tree for given string
void Build(int qs, int qe, int ind, int arr[])
{
    // If start is equal to end then
    // insert the array element
    if (qs == qe) {
        if (arr[qs] == 1) {
            seg[ind].l1 = seg[ind].r1 = qs;
        }
        else {
            seg[ind].l0 = seg[ind].r0 = qs;
        }
 
        lazy[ind] = 0;
        return;
    }
    int mid = (qs + qe) >> 1;
 
    // Build the segment tree
    // for range qs to mid
    Build(qs, mid, ind << 1, arr);
 
    // Build the segment tree
    // for range mid+1 to qe
    Build(mid + 1, qe, ind << 1 | 1, arr);
 
    // merge the two child nodes
    // to obtain the parent node
    seg[ind] = MergeUtil(
        seg[ind << 1],
        seg[ind << 1 | 1]);
}
 
// Query in a range qs to qe
node Query(int qs, int qe,
           int ns, int ne, int ind)
{
    if (lazy[ind] != 0) {
        seg[ind] = UpdateUtil(seg[ind]);
        if (ns != ne) {
            lazy[ind * 2] ^= lazy[ind];
            lazy[ind * 2 + 1] ^= lazy[ind];
        }
        lazy[ind] = 0;
    }
 
    node x;
 
    // If the range lies in this segment
    if (qs <= ns && qe >= ne)
        return seg[ind];
 
    // If the range is out of the bounds
    // of this segment
    if (ne < qs || ns > qe || ns > ne)
        return x;
 
    // Else query for the right and left
    // child node of this subtree
    // and merge them
    int mid = (ns + ne) >> 1;
 
    node l = Query(qs, qe, ns,
                   mid, ind << 1);
    node r = Query(qs, qe,
                   mid + 1, ne,
                   ind << 1 | 1);
 
    x = MergeUtil(l, r);
    return x;
}
 
// range update using lazy prpagation
void RangeUpdate(int us, int ue,
                 int ns, int ne, int ind)
{
    if (lazy[ind] != 0) {
        seg[ind] = UpdateUtil(seg[ind]);
        if (ns != ne) {
            lazy[ind * 2] ^= lazy[ind];
            lazy[ind * 2 + 1] ^= lazy[ind];
        }
        lazy[ind] = 0;
    }
 
    // If the range is out of the bounds
    // of this segment
    if (ns > ne || ns > ue || ne < us)
        return;
 
    // If the range lies in this segment
    if (ns >= us && ne <= ue) {
        seg[ind] = UpdateUtil(seg[ind]);
        if (ns != ne) {
            lazy[ind * 2] ^= 1;
            lazy[ind * 2 + 1] ^= 1;
        }
        return;
    }
 
    // Else query for the right and left
    // child node of this subtree
    // and merge them
    int mid = (ns + ne) >> 1;
    RangeUpdate(us, ue, ns, mid, ind << 1);
    RangeUpdate(us, ue, mid + 1, ne, ind << 1 | 1);
 
    node l = seg[ind << 1], r = seg[ind << 1 | 1];
    seg[ind] = MergeUtil(l, r);
}
 
// Driver code
int main()
{
 
    int arr[] = { 1, 1, 0,
                  1, 0, 1,
                  0, 1, 0,
                  1, 0, 1,
                  1, 0 };
    int n = sizeof(arr) / sizeof(arr[0]);
 
    // Build the segment tree
    Build(0, n - 1, 1, arr);
 
    // Query of Type 2 in the range 3 to 7
    node ans = Query(3, 7, 0, n - 1, 1);
    cout << ans.min1 << "\n";
 
    // Query of Type 3 in the range 2 to 5
    ans = Query(2, 5, 0, n - 1, 1);
    cout << ans.max1 << "\n";
 
    // Query of Type 1 in the range 1 to 4
    RangeUpdate(1, 4, 0, n - 1, 1);
 
    // Query of Type 4 in the range 3 to 7
    ans = Query(3, 7, 0, n - 1, 1);
    cout << ans.min0 << "\n";
 
    // Query of Type 5 in the range 4 to 9
    ans = Query(4, 9, 0, n - 1, 1);
    cout << ans.max0 << "\n";
 
    return 0;
}

chevron_right


Python3

filter_none

edit
close

play_arrow

link
brightness_4
code

# Python program for the given problem
from sys import maxsize
from typing import List
INT_MAX = maxsize
INT_MIN = -maxsize
lazy = [0 for _ in range(100001)]
 
# Class for each node
# in the segment tree
class node:
    def __init__(self) -> None:
        self.l1 = self.r1 = self.l0 = self.r0 = -1
        self.max0 = self.max1 = INT_MIN
        self.min0 = self.min1 = INT_MAX
 
seg = [node() for _ in range(100001)]
 
# A utility function for
# merging two nodes
def MergeUtil(l: node, r: node) -> node:
    x = node()
 
    x.l0 = l.l0 if (l.l0 != -1) else r.l0
    x.r0 = r.r0 if (r.r0 != -1) else l.r0
 
    x.l1 = l.l1 if (l.l1 != -1) else r.l1
    x.r1 = r.r1 if (r.r1 != -1) else l.r1
 
    x.min0 = min(l.min0, r.min0)
    if (l.r0 != -1 and r.l0 != -1):
        x.min0 = min(x.min0, r.l0 - l.r0)
 
    x.min1 = min(l.min1, r.min1)
    if (l.r1 != -1 and r.l1 != -1):
        x.min1 = min(x.min1, r.l1 - l.r1)
 
    x.max0 = max(l.max0, r.max0)
    if (l.l0 != -1 and r.r0 != -1):
        x.max0 = max(x.max0, r.r0 - l.l0)
 
    x.max1 = max(l.max1, r.max1)
    if (l.l1 != -1 and r.r1 != -1):
        x.max1 = max(x.max1, r.r1 - l.l1)
 
    return x
 
# utility function
# for updating a node
def UpdateUtil(x: node) -> node:
    x.l0, x.l1 = x.l1, x.l0
    x.r0, x.r1 = x.r1, x.r0
    x.min1, x.min0 = x.min0, x.min1
    x.max0, x.max1 = x.max1, x.max0
 
    return x
 
# A recursive function that constructs
# Segment Tree for given string
def Build(qs: int, qe: int, ind: int, arr: List[int]) -> None:
 
  # If start is equal to end then
    # insert the array element
    if (qs == qe):
        if (arr[qs] == 1):
            seg[ind].l1 = seg[ind].r1 = qs
        else:
            seg[ind].l0 = seg[ind].r0 = qs
 
        lazy[ind] = 0
        return
 
    mid = (qs + qe) >> 1
 
    # Build the segment tree
    # for range qs to mid
    Build(qs, mid, ind << 1, arr)
 
    # Build the segment tree
    # for range mid+1 to qe
    Build(mid + 1, qe, ind << 1 | 1, arr)
 
    # merge the two child nodes
    # to obtain the parent node
    seg[ind] = MergeUtil(seg[ind << 1], seg[ind << 1 | 1])
 
# Query in a range qs to qe
def Query(qs: int, qe: int, ns: int, ne: int, ind: int) -> node:
    if (lazy[ind] != 0):
        seg[ind] = UpdateUtil(seg[ind])
        if (ns != ne):
            lazy[ind * 2] ^= lazy[ind]
            lazy[ind * 2 + 1] ^= lazy[ind]
        lazy[ind] = 0
    x = node()
 
    # If the range lies in this segment
    if (qs <= ns and qe >= ne):
        return seg[ind]
 
    # If the range is out of the bounds
    # of this segment
    if (ne < qs or ns > qe or ns > ne):
        return x
 
    # Else query for the right and left
    # child node of this subtree
    # and merge them
    mid = (ns + ne) >> 1
    l = Query(qs, qe, ns, mid, ind << 1)
    r = Query(qs, qe, mid + 1, ne, ind << 1 | 1)
    x = MergeUtil(l, r)
    return x
 
# range update using lazy prpagation
def RangeUpdate(us: int, ue: int, ns: int, ne: int, ind: int) -> None:
    if (lazy[ind] != 0):
        seg[ind] = UpdateUtil(seg[ind])
        if (ns != ne):
            lazy[ind * 2] ^= lazy[ind]
            lazy[ind * 2 + 1] ^= lazy[ind]
        lazy[ind] = 0
 
    # If the range is out of the bounds
    # of this segment
    if (ns > ne or ns > ue or ne < us):
        return
 
    # If the range lies in this segment
    if (ns >= us and ne <= ue):
        seg[ind] = UpdateUtil(seg[ind])
        if (ns != ne):
            lazy[ind * 2] ^= 1
            lazy[ind * 2 + 1] ^= 1
        return
 
    # Else query for the right and left
    # child node of this subtree
    # and merge them
    mid = (ns + ne) >> 1
    RangeUpdate(us, ue, ns, mid, ind << 1)
    RangeUpdate(us, ue, mid + 1, ne, ind << 1 | 1)
    l = seg[ind << 1]
    r = seg[ind << 1 | 1]
    seg[ind] = MergeUtil(l, r)
 
# Driver code
if __name__ == "__main__":
    arr = [1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0]
    n = len(arr)
 
    # Build the segment tree
    Build(0, n - 1, 1, arr)
 
    # Query of Type 2 in the range 3 to 7
    ans = Query(3, 7, 0, n - 1, 1)
    print(ans.min1)
 
    # Query of Type 3 in the range 2 to 5
    ans = Query(2, 5, 0, n - 1, 1)
    print(ans.max1)
 
    # Query of Type 1 in the range 1 to 4
    RangeUpdate(1, 4, 0, n - 1, 1)
 
    # Query of Type 4 in the range 3 to 7
    ans = Query(3, 7, 0, n - 1, 1)
    print(ans.min0)
 
    # Query of Type 5 in the range 4 to 9
    ans = Query(4, 9, 0, n - 1, 1)
    print(ans.max0)
 
# This code is contributed by sanjeev2552

chevron_right


Output: 

2
2
3
2

 

competitive-programming-img

My Personal Notes arrow_drop_up
Recommended Articles
Page :