Open In App

Queries to count array elements from a given range having a single set bit

Last Updated : 03 May, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Given an array arr[] consisting of N integers and a 2D array Q[][] consisting of queries of the following two types:

  • 1 L R: Print the count of numbers from the range [L, R] having only a single set bit.
  • 2 X V: Update the array element at Xth index with V.

Examples:

Input: arr[] = { 12, 11, 16, 2, 32 }, Q[][] = { { 1, 0, 2 }, { 2, 4, 24 }, { 1, 1, 4 } }
Output: 1 2
Explanation:

  • Query 1: Print the count of array elements with only a single bit in the range of indices [0, 2], i.e. {16}.
  • Query 2: Set arr[4] = 24. Therefore, the array arr[] modifies to {12, 11, 16, 24, 5}.
  • Query 3: Print the count of array elements with only a single bit in the range of indices [1, 4], i.e. {16, 32}.

Input: arr[] = { 2, 1, 101, 11, 4 }, Q[][] = { { 1, 2, 4 }, { 2, 4, 12 }, { 1, 2, 4 } }
Output: 1 0
Explanation:

  • Query 1: Print the count of array elements with only a single bit in the range of indices [2, 4], i.e. {4}.
  • Query 2: Set arr[4] = 24, which modifies the array to arr[] = { 2, 1, 101, 11, 12}.
  • Query 3: Print the count of array elements with only a single bit in the range of indices [2, 4]. But no array elements from the given range of indices contains only a single bit.

Naive Approach: The simplest approach is to traverse the array over the range of indices [L, R] for each query and for each element, check if it has exactly one set bit or not. Increment count for every array element for which it is found to be true. After traversing the entire range, print the value of count. For queries of type 2, simply arr[X] = V
Time Complexity: O(N * Q * log(N))
Auxiliary Space: O(1)

Efficient Approach: The above approach can be optimized using a Segment Tree. Follow the steps below to solve the problem:

  • Define a function, check(S) to check if integer contains only one set bit.
  • Initialize 3 variables: ss, se, and si to store the starting point of the current segment, ending point of the current segment, and current node value in the segment tree respectively.
  • Define a function, say build_seg(ss, se, si), to build a segment tree Similar to the sum segment tree, each node storing the count of elements with only a single bit in its subtree:
    • If ss == se, tree[s[i]] = check (arr[ss] )
    • Otherwise, traverse the left subtree and right subtree.
    • Now, update the current node as tree[si] = tree[2 * si + 1]+ tree[2 * si + 2].
  • Define a function, say update( ss, se, si, X, V), to point update a value in the array arr[]:
    • If current node is leaf node, i.e ss == se then update the tree[si] = check(V).
    • Otherwise, search for the Xth index i.e if X ? (ss + se) / 2 then Traverse to the left subtree i.e update(ss, mid, 2 * si + 1, X, V). Otherwise, traverse to the right subtree i.e update(mid + 1, se, 2 * si + 2, X, V).
    • Update the current node.
  • Define a function say query(ss, se, si, L, R) to count numbers having only a single bit in the range [L, R]:
    • Check if the current segment [L, R] does not intersect with [ss, se] then return 0.
    • Otherwise, if ss >= L and se ? R then return tree[si].
    • If none of the above conditions satisfies then return query(ss, mid, L, R, 2 * si + 1)+query(mid + 1, se, L, R, 2 * si + 2).
  • For query of type { 1, L, R }, print the query(0, N-1, 0, L, R).
  • For query of type { 2, X, V }, update the value in the tree, update(0, N-1, 0, X, V).

Below is the implementation of the above approach:

C++
// C++ implementation
// for above approach

#include <bits/stdc++.h>
using namespace std;

// Check if N has only
// one set bit
bool check(int N)
{
    if (N == 0)
        return 0;
    return !(N & (N - 1));
}

// Function to build segment tree
void build_seg_tree(int ss, int se, int si,
                    int tree[], int arr[])
{
    // If se is leaf node
    if (ss == se) {

        // Update the node
        tree[si] = check(arr[ss]);
        return;
    }

    // Stores mid value of segment [ss, se]
    int mid = (ss + se) / 2;

    // Recursive call for left Subtree
    build_seg_tree(ss, mid,
                   2 * si + 1, tree, arr);

    // Recursive call for right Subtree
    build_seg_tree(mid + 1, se,
                   2 * si + 2, tree, arr);

    // Update the Current Node
    tree[si] = tree[2 * si + 1]
               + tree[2 * si + 2];
}

// Function to update a value at Xth index
void update(int ss, int se, int si,
            int X, int V, int tree[], int arr[])
{

    if (ss == se) {

        // If ss is equal to X
        if (ss == X) {

            // Update the Xth node
            arr[X] = V;

            // Update the tree
            tree[si] = check(V);
        }
        return;
    }

    // Stores the mid of segment [ss, se]
    int mid = (ss + se) / 2;

    if (X <= mid)
        update(ss, mid, 2 * si + 1,
               X, V, tree, arr);
    else
        update(mid + 1, se, 2 * si + 2,
               X, V, tree, arr);

    // Update current node
    tree[si] = tree[2 * si + 1]
               + tree[2 * si + 2];
}

// Count of numbers
// having one set bit
int query(int l, int r, int ss,
          int se, int si, int tree[])
{
    // If L > se or R < ss
    // Invalid Range
    if (r < ss || l > se)
        return 0;

    // If [ss, se] lies
    // inside the [L, R]
    if (l <= ss && r >= se)
        return tree[si];

    // Stores the mid of segment [ss, se]
    int mid = (ss + se) / 2;

    // Return the sum after recursively
    // traversing left and right subtree
    return query(l, r, ss,
                 mid, 2 * si + 1, tree)
           + query(l, r, mid + 1,
                   se, 2 * si + 2, tree);
}

// Function to solve queries
void Query(int arr[], int N,
           vector<vector<int> > Q)
{
    // Initialise Segment tree
    int tree[4 * N] = { 0 };

    // Build segment tree
    build_seg_tree(0, N - 1, 0, tree, arr);

    // Perform queries
    for (int i = 0; i < (int)Q.size(); i++) {
        if (Q[i][0] == 1)

            cout << query(Q[i][1], Q[i][2],
                          0, N - 1, 0, tree)
                 << " ";
        else
            update(0, N - 1, 0, Q[i][1], Q[i][2], tree,
                   arr);
    }
}

// Driver Code
int main()
{
    // Input
    int arr[] = { 12, 11, 16, 2, 32 };
    vector<vector<int> > Q{ { 1, 0, 2 },
                            { 2, 4, 24 },
                            { 1, 1, 4 } };
    int N = sizeof(arr) / sizeof(arr[0]);

    // Function call to
    // solve queries
    Query(arr, N, Q);

    return 0;
}
Java
/*package whatever //do not write package name here */
import java.io.*;
class GFG {

  // Check if N has only
  // one set bit
  static int check(int N)
  {
    if (Integer.bitCount(N) == 1)
      return 1;
    else
      return 0;
  }

  // Function to build segment tree
  static void build_seg_tree(int ss, int se, int si,
                             int tree[], int arr[])
  {

    // If se is leaf node
    if (ss == se) 
    {

      // Update the node
      tree[si] = check(arr[ss]);
      return;
    }

    // Stores mid value of segment [ss, se]
    int mid = (ss + se) / 2;

    // Recursive call for left Subtree
    build_seg_tree(ss, mid, 2 * si + 1, tree, arr);

    // Recursive call for right Subtree
    build_seg_tree(mid + 1, se, 2 * si + 2, tree, arr);

    // Update the Current Node
    tree[si] = tree[2 * si + 1] + tree[2 * si + 2];
  }

  // Function to update a value at Xth index
  static void update(int ss, int se, int si, int X, int V,
                     int tree[], int arr[])
  {

    if (ss == se) 
    {

      // If ss is equal to X
      if (ss == X) 
      {

        // Update the Xth node
        arr[X] = V;

        // Update the tree
        tree[si] = check(V);
      }
      return;
    }

    // Stores the mid of segment [ss, se]
    int mid = (ss + se) / 2;

    if (X <= mid)
      update(ss, mid, 2 * si + 1, X, V, tree, arr);
    else
      update(mid + 1, se, 2 * si + 2, X, V, tree,
             arr);

    // Update current node
    tree[si] = tree[2 * si + 1] + tree[2 * si + 2];
  }

  // Count of numbers
  // having one set bit
  static int query(int l, int r, int ss, 
                   int se, int si,
                   int tree[])
  {
    // If L > se or R < ss
    // Invalid Range
    if (r < ss || l > se)
      return 0;

    // If [ss, se] lies
    // inside the [L, R]
    if (l <= ss && r >= se)
      return tree[si];

    // Stores the mid of segment [ss, se]
    int mid = (ss + se) / 2;

    // Return the sum after recursively
    // traversing left and right subtree
    return query(l, r, ss, mid, 2 * si + 1, tree)
      + query(l, r, mid + 1, se, 2 * si + 2, tree);
  }

  // Function to solve queries
  static void Query(int arr[], int N, int[][] Q)
  {
    // Initialise Segment tree
    int tree[] = new int[4 * N];

    // Build segment tree
    build_seg_tree(0, N - 1, 0, tree, arr);

    // Perform queries
    for (int i = 0; i < Q.length; i++) {
      if (Q[i][0] == 1)

        System.out.print(query(Q[i][1], Q[i][2], 0,
                               N - 1, 0, tree) + " ");
      else
        update(0, N - 1, 0, Q[i][1], Q[i][2], tree,
               arr);
    }
  }

  // Driver Code
  public static void main(String[] args)
  {

    int arr[] = { 12, 11, 16, 2, 32 };
    int[][] Q
      = { { 1, 0, 2 }, { 2, 4, 24 }, { 1, 1, 4 } };
    int N = arr.length;

    // Function call to
    // solve queries
    Query(arr, N, Q);
  }
}

// This code is contributed by hemanthsawarna1506.
Python
# Python3 implementation of
# the above approach 

# Check if N has only
# one set bit
def check(N) :
    
    if (N == 0):
        return 0
    return ((N & (N - 1)) == 0)

# Function to build segment tree
def build_seg_tree(ss, se, si, tree, arr) :
    
    # If se is leaf node
    if (ss == se) :

        # Update the node
        tree[si] = check(arr[ss])
        return
    
    # Stores mid value of segment [ss, se]
    mid = (ss + se) // 2

    # Recursive call for left Subtree
    build_seg_tree(ss, mid,
                   2 * si + 1, tree, arr)

    # Recursive call for right Subtree
    build_seg_tree(mid + 1, se,
                   2 * si + 2, tree, arr)

    # Update the Current Node
    tree[si] = tree[2 * si + 1] + tree[2 * si + 2]

# Function to update a value at Xth index
def update(ss, se, si, X, V, tree, arr) :
    if (ss == se) :

        # If ss is equal to X
        if (ss == X) :

            # Update the Xth node
            arr[X] = V

            # Update the tree
            tree[si] = check(V)    
        return
    
    # Stores the mid of segment [ss, se]
    mid = (ss + se) // 2

    if (X <= mid):
        update(ss, mid, 2 * si + 1,
               X, V, tree, arr)
    else :
        update(mid + 1, se, 2 * si + 2,
               X, V, tree, arr)

    # Update current node
    tree[si] = tree[2 * si + 1] + tree[2 * si + 2]

# Count of numbers
# having one set bit
def query(l, r, ss, se, si, tree) :
              
    # If L > se or R < ss
    # Invalid Range
    if (r < ss or l > se):
        return 0

    # If [ss, se] lies
    # inside the [L, R]
    if (l <= ss and r >= se):
        return tree[si]

    # Stores the mid of segment [ss, se]
    mid = (ss + se) // 2

    # Return the sum after recursively
    # traversing left and right subtree
    return (query(l, r, ss, mid, 2 * si + 1, tree) + query(l, r, mid + 1, se, 2 * si + 2, tree))

# Function to solve queries
def Query(arr, N, Q) :
    
    # Initialise Segment tree
    tree = [0] * (4 * N)

    # Build segment tree
    build_seg_tree(0, N - 1, 0, tree, arr)

    # Perform queries
    for i in range(len(Q)):
        if (Q[i][0] == 1):

            print(query(Q[i][1], Q[i][2],
                          0, N - 1, 0, tree), end = " ")
        else :
            update(0, N - 1, 0, Q[i][1], Q[i][2], tree, arr)
    
# Driver Code

# Input
arr = [ 12, 11, 16, 2, 32 ]
Q = [ [ 1, 0, 2 ], [ 2, 4, 24 ], [ 1, 1, 4 ]]  
N = len(arr)

# Function call to
# solve queries
Query(arr, N, Q)

# This code is contributed by code_hunt.
C#
// C# implementation 
// for above approach 
using System;
using System.Collections.Generic;
class GFG 
{

  // Check if N has only 
  // one set bit 
  static int check(int N) 
  { 
    if (N == 0 || (N & (N - 1)) != 0) 
      return 0; 

    return 1;
  } 

  // Function to build segment tree 
  static void build_seg_tree(int ss, int se, int si, 
                             int[] tree, int[] arr) 
  { 
    
    // If se is leaf node 
    if (ss == se) 
    { 

      // Update the node 
      tree[si] = check(arr[ss]); 
      return; 
    } 

    // Stores mid value of segment [ss, se] 
    int mid = (ss + se) / 2; 

    // Recursive call for left Subtree 
    build_seg_tree(ss, mid, 
                   2 * si + 1, tree, arr); 

    // Recursive call for right Subtree 
    build_seg_tree(mid + 1, se, 
                   2 * si + 2, tree, arr); 

    // Update the Current Node 
    tree[si] = tree[2 * si + 1] 
      + tree[2 * si + 2]; 
  } 

  // Function to update a value at Xth index 
  static void update(int ss, int se, int si, 
                     int X, int V, int[] tree, int[] arr) 
  { 

    if (ss == se)
    { 

      // If ss is equal to X 
      if (ss == X)
      { 

        // Update the Xth node 
        arr[X] = V; 

        // Update the tree 
        tree[si] = check(V); 
      } 
      return; 
    } 

    // Stores the mid of segment [ss, se] 
    int mid = (ss + se) / 2; 

    if (X <= mid) 
      update(ss, mid, 2 * si + 1, 
             X, V, tree, arr); 
    else
      update(mid + 1, se, 2 * si + 2, 
             X, V, tree, arr); 

    // Update current node 
    tree[si] = tree[2 * si + 1] 
      + tree[2 * si + 2]; 
  } 

  // Count of numbers 
  // having one set bit 
  static int query(int l, int r, int ss, 
                   int se, int si, int[] tree) 
  { 

    // If L > se or R < ss 
    // Invalid Range 
    if (r < ss || l > se) 
      return 0; 

    // If [ss, se] lies 
    // inside the [L, R] 
    if (l <= ss && r >= se) 
      return tree[si]; 

    // Stores the mid of segment [ss, se] 
    int mid = (ss + se) / 2; 

    // Return the sum after recursively 
    // traversing left and right subtree 
    return query(l, r, ss, 
                 mid, 2 * si + 1, tree) 
      + query(l, r, mid + 1, 
              se, 2 * si + 2, tree); 
  } 

  // Function to solve queries 
  static void Query(int[] arr, int N, 
                    List<List<int> > Q) 
  { 

    // Initialise Segment tree 
    int[] tree = new int[4 * N]; 

    // Build segment tree 
    build_seg_tree(0, N - 1, 0, tree, arr); 

    // Perform queries 
    for (int i = 0; i < (int)Q.Count; i++)
    { 
      if (Q[i][0] == 1)       
        Console.Write(query(Q[i][1], Q[i][2], 0, N - 1, 0, tree) + " ");
      else
        update(0, N - 1, 0, Q[i][1], Q[i][2], tree, arr); 
    } 
  }  

  // Driver code
  static void Main() 
  {

    // Input 
    int[] arr = { 12, 11, 16, 2, 32 }; 
    List<List<int> > Q = new List<List<int>>();
    Q.Add(new List<int>(new int[]{1, 0, 2}));
    Q.Add(new List<int>(new int[]{2, 4, 24}));
    Q.Add(new List<int>(new int[]{1, 1, 4})); 
    int N = arr.Length; 

    // Function call to 
    // solve queries 
    Query(arr, N, Q);
  }
}

// This code is contributed by divyeshrabadiya07
Javascript
<script>

// JavaScript implementation
// for above approach


// Check if N has only
// one set bit
function check( N)
{
    if (N == 0)
        return 0;
    return !(N & (N - 1));
}

// Function to build segment tree
function build_seg_tree( ss,  se,  si, tree, arr)
{
    // If se is leaf node
    if (ss == se) {

        // Update the node
        tree[si] = check(arr[ss]);
        return;
    }

    // Stores mid value of segment [ss, se]
    var mid = parseInt((ss + se) / 2);

    // Recursive call for left Subtree
    build_seg_tree(ss, mid, 2 * si + 1, tree, arr);

    // Recursive call for right Subtree
    build_seg_tree(mid + 1, se, 2 * si + 2, tree, arr);

    // Update the Current Node
    tree[si] = tree[2 * si + 1] + tree[2 * si + 2];
}

// Function to update a value at Xth index
function update(ss, se, si, X, V, tree,  arr)
{

    if (ss == se) {

        // If ss is equal to X
        if (ss == X) {

            // Update the Xth node
            arr[X] = V;

            // Update the tree
            tree[si] = check(V);
        }
        return;
    }

    // Stores the mid of segment [ss, se]
    var mid = parseInt((ss + se) / 2);

    if (X <= mid)
        update(ss, mid, 2 * si + 1, X, V, tree, arr);
    else
        update(mid + 1, se, 2 * si + 2, X, V, tree, arr);

    // Update current node
    tree[si] = tree[2 * si + 1] + tree[2 * si + 2];
}

// Count of numbers
// having one set bit
function query( l, r, ss, se, si, tree)
{
    // If L > se or R < ss
    // Invalid Range
    if (r < ss || l > se)
        return 0;

    // If [ss, se] lies
    // inside the [L, R]
    if (l <= ss && r >= se)
        return tree[si];

    // Stores the mid of segment [ss, se]
    var mid = parseInt((ss + se) / 2);

    // Return the sum after recursively
    // traversing left and right subtree
    return query(l, r, ss, mid, 2 * si + 1, tree)
           + query(l, r, mid + 1, se, 2 * si + 2, tree);
}

// Function to solve queries
function Query(arr, N, Q)
{
    // Initialise Segment tree
    var tree = new Array(4 * N);
    tree.fill( 0 );

    // Build segment tree
    build_seg_tree(0, N - 1, 0, tree, arr);

    // Perform queries
    for (var i = 0; i < Q.length; i++) {
        if (Q[i][0] == 1)

            document.write( query(Q[i][1], Q[i][2],
                          0, N - 1, 0, tree)
                 + " ");
        else
            update(0, N - 1, 0, Q[i][1], Q[i][2], tree,
                   arr);
    }
}

var arr = [ 12, 11, 16, 2, 32 ];
var Q = [ [1, 0, 2 ],
          [ 2, 4, 24],
          [ 1, 1, 4 ] ];
    var N = arr.length;

    // Function call to
    // solve queries
    Query(arr, N, Q);

// This code is contributed by SoumikMondal

</script>

Output
1 2 

Time Complexity: O(N*log(N)+Q*log(N))
Auxiliary Space: O(N) 

Approach using Fenwick Tree

To optimize the process of handling the two types of queries, we can utilize a Fenwick Tree (Binary Indexed Tree, BIT). This tree structure is especially effective for cumulative frequency tables and can efficiently update elements and query prefix sums. In this scenario, we maintain a BIT that holds counts of numbers with a single set bit. This allows us to achieve faster updates and range queries, reducing complexity compared to a Segment Tree.

Why Use Fenwick Tree for this Problem:

  • Faster Updates: The Fenwick Tree provides a way to update an element in logarithmic time.
  • Efficient Range Queries: It allows sum queries (or in our case, count queries) over a range in logarithmic time after an O(N log N) preprocessing time.
  • Space Efficiency: It uses space proportional to the array size, which is optimal for the operations it supports.

Steps in the Solution:

  • Initial Construction: Convert the initial array into a form suitable for the Fenwick Tree, marking each position based on whether the number has a single set bit.
  • Update Function: Modify the tree when an update occurs, ensuring the counts reflect the change.
  • Query Function: Efficiently compute the number of elements with a single set bit in any given range.
Python
def is_power_of_two(x):
    # Returns True if x is a power of two, checked by bitwise operation
    # (x & (x - 1)) == 0 checks if x has only one bit set, which is true for powers of two
    return (x != 0) and (x & (x - 1)) == 0

def update(fenwick, idx, value, n):
    # Updates the Fenwick tree (binary indexed tree) at index 'idx' by 'value'
    # 'n' is the size of the original array for bounds checking
    while idx <= n:
        fenwick[idx] += value  # Increment the current index by the value
        idx += idx & -idx  # Move to the next index that is responsible for this index

def build_fenwick(arr):
    # Constructs and returns a Fenwick tree from the given array 'arr'
    # Only counts 1 if the element is a power of two
    n = len(arr)
    fenwick = [0] * (n + 1)  # Fenwick tree size is n + 1
    for i in range(n):
        if is_power_of_two(arr[i]):
            update(fenwick, i + 1, 1, n)  # Update Fenwick tree if the element is a power of two
    return fenwick

def sum_query(fenwick, idx):
    # Computes the prefix sum from the start to the 'idx' in the Fenwick tree
    s = 0
    while idx > 0:
        s += fenwick[idx]  # Add current index value to sum
        idx -= idx & -idx  # Move to next responsible index
    return s

def range_query(fenwick, l, r):
    # Computes the range sum from index 'l' to 'r' in the original array using the Fenwick tree
    return sum_query(fenwick, r + 1) - sum_query(fenwick, l)  # Utilize prefix sums for range sum

def handle_queries(arr, queries):
    # Processes each query on the array 'arr' using a Fenwick tree for efficient range queries and updates
    fenwick = build_fenwick(arr)  # Initial Fenwick tree based on array 'arr'
    n = len(arr)
    results = []  # To store results of type 1 queries
    for query in queries:
        if query[0] == 1:
            # Query of type 1: Count elements with single set bit between indices 'l' and 'r'
            l, r = query[1], query[2]
            results.append(range_query(fenwick, l, r))
        elif query[0] == 2:
            # Query of type 2: Update element at index 'idx' with value 'val'
            idx, val = query[1], query[2]
            current_val = 1 if is_power_of_two(arr[idx]) else 0  # Check if current arr[idx] is a power of two
            new_val = 1 if is_power_of_two(val) else 0  # Check if new value 'val' is a power of two
            update(fenwick, idx + 1, new_val - current_val, n)  # Update the Fenwick tree
            arr[idx] = val  # Update the array
    return results

# Example usage
arr = [12, 11, 16, 2, 32]
queries = [[1, 0, 2], [2, 4, 24], [1, 1, 4]]
result = handle_queries(arr, queries)
print(result)  # Output: [1, 2]

Output
[1, 2]

Time Complexity: O((N+Q)*logN) for the initial construction and O(logN) for each update and query, leading to O((N+Q)logN) overall.

Auxilary Space: O(N) for storing the Fenwick Tree.




Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads