Skip to content
Related Articles
Open in App
Not now

Related Articles

Range queries to count 1s in a subarray after flip operations

Improve Article
Save Article
Like Article
  • Difficulty Level : Expert
  • Last Updated : 01 Feb, 2022
Improve Article
Save Article
Like Article

Given an array ‘arr’ all the numbers of which are initialized to ‘0’ then the array can be updated at any time in the following way: 
All the elements of any sub-array from the array can be flipped i.e. ‘1’ => ‘0’ or ‘0’ => ‘1’. 
The task is to find the number of 1s from the array for any incoming query [l, r] where ‘l’ and ‘r’ are valid indices in the given array.

Examples:  

Input: n = 7
arr = {0, 0, 0, 0, 0, 0, 0}
Update from index '2' to '5', {0, 0, 1, 1, 1, 1, 0}
Query from index '1' to '6'
Update from index '1' to '3', {0, 1, 0, 0, 1, 1, 0}
Query from index '1' to '4'
Output:
4
2

Input: n = 5
arr = {0, 0, 0, 0, 0}
Update from index '0' to '2', {1, 1, 1, 0, 0}
Query from index '2' to '4'
Update from index '2' to '4', {1, 1, 0, 1, 1}
Query from index '0' to '3'
Output:
1
3

Approach: This problem can be solved using segment trees, you can read more about segment trees here.  

  • Initialize the segment tree with value ‘0’ at all the nodes.
  • At any update, use update function to store updated values at that index only so that queries can be performed in O(Log n) time which is known as Lazy Propagation Method.
  • While building the segment tree, use merge function such that: 
    • If pending updates are present in left and right subArrays, increment count by leftSub.end – leftSub.start + 1 – leftSub.count and rightSub.end – rightSub.start + 1 – rightSub.count respectively.
    • Otherwise, add leftSub.count and rightSub.count.

Below is the implementation of the above approach: 

C++




// C++ implementation of the approach
#include <bits/stdc++.h>
 
// Structure which contains
// all the necessary functions
// to solve a segment tree
struct SegmentTemplate {
    int start, end;
    int count = 0;
    bool pendingUpdate = false;
 
    // The function which
    // contains merge logic
    void merge(SegmentTemplate& leftSub,
              SegmentTemplate& rightSub)
    {
        // If the left subarray
        // contains some pending update
        if (leftSub.pendingUpdate)
            count += leftSub.end - leftSub.start +
                                 1 - leftSub.count;
        else
            count += leftSub.count;
 
        // If the right subarray
        // contains some pending update
        if (rightSub.pendingUpdate)
            count += rightSub.end - rightSub.start +
                                 1 - rightSub.count;
        else
            count += rightSub.count;
    }
 
    // To return the count
    int query()
    {
        return count;
    }
 
    // If the segment tree
    // has pending update
    bool hasPendingUpdate()
    {
        return pendingUpdate;
    }
 
    // Apply pending update
    void applyPendingUpdate()
    {
        count = end - start + 1 - count;
        pendingUpdate = false;
    }
 
    // For a node to
    // add pending update
    void addUpdate()
    {
        pendingUpdate = !pendingUpdate;
    }
};
 
// The class that performs
// all the segment tree functions
class SegmentTree {
    SegmentTemplate* treeBuild;
    int N;
 
public:
    SegmentTree(int N)
    {
        this->N = N;
        treeBuild = new SegmentTemplate[getSegmentTreeSize(N)];
        buildTree(1, 0, N - 1);
    }
 
    // For the query
    int query(int start, int end)
    {
 
        // Stores the result
        SegmentTemplate result
            = query(1, start, end);
        return result.query();
    }
 
    // Updates at the
    // specific location
    void update(int start, int end)
    {
        update(1, start, end);
    }
 
private:
    void buildTree(int stIndex, int start, int end)
    {
 
        // Defining index in treeBuild
        treeBuild[stIndex].start
            = start,
            treeBuild[stIndex].end = end;
 
        if (start == end) {
            return;
        }
 
        // Dividing the array into two
        // parts for assigning the indices
        // and building segment tree
        int mid = (start + end) / 2,
            leftChildIndex = 2 * stIndex,
            rightChildIndex = leftChildIndex + 1;
 
        buildTree(leftChildIndex, start, mid);
        buildTree(rightChildIndex, mid + 1, end);
 
        // Combine the array
        treeBuild[stIndex]
            .merge(treeBuild[leftChildIndex],
                   treeBuild[rightChildIndex]);
    }
 
    // Gets the segment tree size
    int getSegmentTreeSize(int N)
    {
        int size = 1;
        for (; size < N; size <<= 1)
            ;
        return size << 1;
    }
 
    // Function for getting
    // to know the queries
    SegmentTemplate query(int stIndex, int start, int end)
    {
 
        // When reaches a particular index
        if (treeBuild[stIndex].start == start
            && treeBuild[stIndex].end == end) {
            SegmentTemplate result = treeBuild[stIndex];
            if (result.hasPendingUpdate())
                result.applyPendingUpdate();
            return result;
        }
 
        // Dividing the array into two
        // parts for assigning the indices
        // and querying segment trees
        int mid = (treeBuild[stIndex].start
                   + treeBuild[stIndex].end)
                  / 2,
            leftChildIndex = stIndex * 2,
            rightChildIndex = leftChildIndex + 1;
        SegmentTemplate result;
 
        if (start > mid)
            result = query(rightChildIndex, start, end);
        else if (end <= mid)
            result = query(leftChildIndex, start, end);
        else {
            SegmentTemplate leftResult = query(leftChildIndex, start, mid),
                        rightResult = query(rightChildIndex, mid + 1, end);
            result.start = leftResult.start;
            result.end = rightResult.end;
            result.merge(leftResult, rightResult);
        }
 
        // If tree has update,
        // apply the pending update
        if (treeBuild[stIndex].hasPendingUpdate()) {
            result.addUpdate();
            result.applyPendingUpdate();
        }
        return result;
    }
 
    // Function for updating the tree
    void update(int stIndex, int start, int end)
    {
 
        // Adds the update in O(1)
        // time to the node
        if (treeBuild[stIndex].start == start
            && treeBuild[stIndex].end == end) {
            treeBuild[stIndex].addUpdate();
            return;
        }
 
        // Dividing the array into two
        // parts for assigning the indices
        // and querying segment trees
        int mid = (treeBuild[stIndex].start
                   + treeBuild[stIndex].end)
                  / 2,
            leftChildIndex = stIndex * 2,
            rightChildIndex = leftChildIndex + 1;
 
        if (start > mid)
            update(rightChildIndex, start, end);
        else if (end <= mid)
            update(leftChildIndex, start, end);
        else {
            update(leftChildIndex, start, mid);
            update(rightChildIndex, mid + 1, end);
        }
 
        // Calling the build function
        // for building the segment tree
        treeBuild[stIndex]
            .merge(treeBuild[leftChildIndex],
                   treeBuild[rightChildIndex]);
    }
};
 
// Driver function
int main()
{
    int N = 7;
    SegmentTree st(N);
 
    // Updating the segment tree
    st.update(2, 5);
 
    // Executing the query
    printf("%d\n", st.query(1, 6));
 
    // Updating the segment tree
    st.update(1, 3);
 
    // Executing the query
    printf("%d\n", st.query(1, 6));
    return 0;
}

Python3




# Python3 implementation of the approach
 
# Structure which contains
# all the necessary functions
# to solve a segment tree
class SegmentTemplate:
     
    def __init__(self) -> None:
         
        self.start = 0
        self.end = 0
        self.count = 0
        self.pendingUpdate = False
 
    # The function which
    # contains merge logic
    def merge(self, leftSub, rightSub) -> None:
 
        # If the left subarray
        # contains some pending update
        if (leftSub.pendingUpdate):
            self.count += (leftSub.end - leftSub.start +
                                     1 - leftSub.count)
        else:
            self.count += leftSub.count
 
        # If the right subarray
        # contains some pending update
        if (rightSub.pendingUpdate):
            self.count += (rightSub.end - rightSub.start +
                                      1 - rightSub.count)
        else:
            self.count += rightSub.count
 
    # To return the count
    def query(self) -> int:
 
        return self.count
 
    # If the segment tree
    # has pending update
    def hasPendingUpdate(self) -> bool:
 
        return self.pendingUpdate
 
    # Apply pending update
    def applyPendingUpdate(self) -> None:
 
        self.count = self.end - self.start + 1 - self.count
        self.pendingUpdate = False
 
    # For a node to
    # add pending update
    def addUpdate(self) -> None:
 
        self.pendingUpdate = not self.pendingUpdate
 
# The class that performs
# all the segment tree functions
class SegmentTree:
     
    def __init__(self, N) -> None:
         
        self.treeBuild = [SegmentTemplate() for _ in range(
            self._getSegmentTreeSize(N))]
        self.N = N
        self._buildTree(1, 0, N - 1)
 
    # For the query
    def query(self, start: int, end: int) -> int:
 
        # Stores the result
        result = self._query(1, start, end)
        return result.query()
 
    # Updates at the
    # specific location
    def update(self, start: int, end: int) -> None:
 
        self._update(1, start, end)
 
    def _buildTree(self, stIndex: int, start: int, end: int) -> None:
 
        # Defining index in treeBuild
        self.treeBuild[stIndex].start = start
        self.treeBuild[stIndex].end = end
 
        if (start == end):
            return
 
        # Dividing the array into two
        # parts for assigning the indices
        # and building segment tree
        mid = (start + end) // 2
        leftChildIndex = 2 * stIndex
        rightChildIndex = leftChildIndex + 1
 
        self._buildTree(leftChildIndex, start, mid)
        self._buildTree(rightChildIndex, mid + 1, end)
 
        # Combine the array
        self.treeBuild[stIndex].merge(self.treeBuild[leftChildIndex],
                                      self.treeBuild[rightChildIndex])
 
    # Gets the segment tree size
    def _getSegmentTreeSize(self, N: int) -> int:
 
        size = 1
        while size < N:
            size <<= 1
             
        return size << 1
 
    # Function for getting
    # to know the queries
    def _query(self, stIndex: int,
             start: int, end: int) -> SegmentTemplate:
 
        # When reaches a particular index
        if (self.treeBuild[stIndex].start == start and
            self.treeBuild[stIndex].end == end):
            result = self.treeBuild[stIndex]
            if (result.hasPendingUpdate()):
                result.applyPendingUpdate()
                 
            return result
 
        # Dividing the array into two
        # parts for assigning the indices
        # and querying segment trees
        mid = (self.treeBuild[stIndex].start +
               self.treeBuild[stIndex].end) // 2
                
        leftChildIndex = stIndex * 2
        rightChildIndex = leftChildIndex + 1
        result = SegmentTemplate()
 
        if (start > mid):
            result = self._query(rightChildIndex, start, end)
        elif (end <= mid):
            result = self._query(leftChildIndex, start, end)
        else:
            leftResult = self._query(leftChildIndex, start, mid)
            rightResult = self._query(rightChildIndex, mid + 1, end)
            result.start = leftResult.start
            result.end = rightResult.end
            result.merge(leftResult, rightResult)
 
        # If tree has update,
        # apply the pending update
        if (self.treeBuild[stIndex].hasPendingUpdate()):
            result.addUpdate()
            result.applyPendingUpdate()
 
        return result
 
    # Function for updating the tree
    def _update(self, stIndex: int, start: int, end: int) -> None:
 
        # Adds the update in O(1)
        # time to the node
        if (self.treeBuild[stIndex].start == start and
            self.treeBuild[stIndex].end == end):
            self.treeBuild[stIndex].addUpdate()
            return
 
        # Dividing the array into two
        # parts for assigning the indices
        # and querying segment trees
        mid = (self.treeBuild[stIndex].start +
               self.treeBuild[stIndex].end) // 2
                
        leftChildIndex = stIndex * 2
        rightChildIndex = leftChildIndex + 1
 
        if (start > mid):
            self._update(rightChildIndex, start, end)
        elif (end <= mid):
            self._update(leftChildIndex, start, end)
        else:
            self._update(leftChildIndex, start, mid)
            self._update(rightChildIndex, mid + 1, end)
 
        # Calling the build function
        # for building the segment tree
        self.treeBuild[stIndex].merge(self.treeBuild[leftChildIndex],
                                      self.treeBuild[rightChildIndex])
 
# Driver code
if __name__ == "__main__":
 
    N = 7
    st = SegmentTree(N)
 
    # Updating the segment tree
    st.update(2, 5)
 
    # Executing the query
    print("{}".format(st.query(1, 6)))
 
    # Updating the segment tree
    st.update(1, 3)
 
    # Executing the query
    print("{}".format(st.query(1, 6)))
 
# This code is contributed by sanjeev2552

Output: 

4
3

 


My Personal Notes arrow_drop_up
Like Article
Save Article
Related Articles

Start Your Coding Journey Now!