Range Update Queries to XOR with 1 in a Binary Array.


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:

    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

    
    

  3. 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
    

    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
    
  4. 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:

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


Output:

2
2
3
2

competitive-programming-img




My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.