Open In App

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

Last Updated : 21 May, 2024
Comments
Improve
Suggest changes
Like Article
Like
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.
C++
#include <iostream>
#include <vector>

// Function to check if a number is a power of two using
// bitwise operation
bool isPowerOfTwo(int x)
{
    // x & (x - 1) removes the lowest set bit, if zero then
    // it's power of two
    return (x != 0) && ((x & (x - 1)) == 0);
}

// Function to update a value in Fenwick Tree
void update(std::vector<int>& fenwick, int idx, int value,
            int n)
{
    // Start from index 'idx' and update all relevant
    // indices in the tree
    while (idx <= n) {
        fenwick[idx]
            += value; // Increment the value at this index
        idx += idx
               & -idx; // Move to next index to be updated
    }
}

// Function to build the Fenwick Tree from the given array
std::vector<int> buildFenwick(const std::vector<int>& arr)
{
    int n = arr.size();
    std::vector<int> fenwick(n + 1, 0);
    for (int i = 0; i < n; ++i) {
        if (isPowerOfTwo(arr[i])) {
            update(fenwick, i + 1, 1,
                   n); // Only count 1 if the element is a
                       // power of two
        }
    }
    return fenwick;
}

// Function to get the sum from index 1 to 'idx' in the
// Fenwick Tree
int sumQuery(const std::vector<int>& fenwick, int idx)
{
    int sum = 0;
    while (idx > 0) {
        sum += fenwick[idx]; // Add the current index's
                             // value to the sum
        idx -= idx & -idx; // Move to the next contributing
                           // element in the tree
    }
    return sum;
}

// Function to get the sum between indices 'l' and 'r' in
// the original array
int rangeQuery(const std::vector<int>& fenwick, int l,
               int r)
{
    return sumQuery(fenwick, r + 1) - sumQuery(fenwick, l);
}

// Function to handle queries and updates on the array
std::vector<int>
handleQueries(std::vector<int>& arr,
              const std::vector<std::vector<int> >& queries)
{
    std::vector<int> fenwick = buildFenwick(arr);
    std::vector<int> results;
    int n = arr.size();

    for (const auto& query : queries) {
        if (query[0] == 1) {
            // Query type 1: count elements with single set
            // bit between l and r
            int l = query[1], r = query[2];
            results.push_back(rangeQuery(fenwick, l, r));
        }
        else if (query[0] == 2) {
            // Query type 2: update element at index idx
            // with value val
            int idx = query[1], val = query[2];
            int current_val
                = isPowerOfTwo(arr[idx]) ? 1 : 0;
            int new_val = isPowerOfTwo(val) ? 1 : 0;
            update(fenwick, idx + 1, new_val - current_val,
                   n);
            arr[idx] = val;
        }
    }
    return results;
}

// Driver code
int main()
{
    std::vector<int> arr = { 12, 11, 16, 2, 32 };
    std::vector<std::vector<int> > queries
        = { { 1, 0, 2 }, { 2, 4, 24 }, { 1, 1, 4 } };
    std::vector<int> result = handleQueries(arr, queries);
    for (int res : result) {
        std::cout << res << " ";
    }
    std::cout << std::endl;
    return 0;
}
Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    // Function to check if a number is a power of two using
    // bitwise operation
    static boolean isPowerOfTwo(int x)
    {
        // x & (x - 1) removes the lowest set bit, if zero
        // then it's power of two
        return (x != 0) && ((x & (x - 1)) == 0);
    }

    // Function to update a value in Fenwick Tree
    static void update(List<Integer> fenwick, int idx,
                       int value)
    {
        // Start from index 'idx' and update all relevant
        // indices in the tree
        while (idx < fenwick.size()) {
            fenwick.set(idx,
                        fenwick.get(idx)
                            + value); // Increment the value
                                      // at this index
            idx += idx & -idx; // Move to next index to be
                               // updated
        }
    }

    // Function to build the Fenwick Tree from the given
    // array
    static List<Integer> buildFenwick(List<Integer> arr)
    {
        int n = arr.size();
        List<Integer> fenwick = new ArrayList<>(n + 1);
        for (int i = 0; i <= n; i++) {
            fenwick.add(0);
        }
        for (int i = 0; i < n; ++i) {
            if (isPowerOfTwo(arr.get(i))) {
                update(fenwick, i + 1,
                       1); // Only count 1 if the element is
                           // a power of two
            }
        }
        return fenwick;
    }

    // Function to get the sum from index 1 to 'idx' in the
    // Fenwick Tree
    static int sumQuery(List<Integer> fenwick, int idx)
    {
        int sum = 0;
        while (idx > 0) {
            sum += fenwick.get(
                idx); // Add the current index's value to
                      // the sum
            idx -= idx
                   & -idx; // Move to the next contributing
                           // element in the tree
        }
        return sum;
    }

    // Function to get the sum between indices 'l' and 'r'
    // in the original array
    static int rangeQuery(List<Integer> fenwick, int l,
                          int r)
    {
        return sumQuery(fenwick, r + 1)
            - sumQuery(fenwick, l);
    }

    // Function to handle queries and updates on the array
    static List<Integer>
    handleQueries(List<Integer> arr,
                  List<List<Integer> > queries)
    {
        List<Integer> fenwick = buildFenwick(arr);
        List<Integer> results = new ArrayList<>();
        int n = arr.size();

        for (List<Integer> query : queries) {
            if (query.get(0) == 1) {
                // Query type 1: count elements with single
                // set bit between l and r
                int l = query.get(1), r = query.get(2);
                results.add(rangeQuery(fenwick, l, r));
            }
            else if (query.get(0) == 2) {
                // Query type 2: update element at index idx
                // with value val
                int idx = query.get(1), val = query.get(2);
                int currentVal
                    = isPowerOfTwo(arr.get(idx)) ? 1 : 0;
                int newVal = isPowerOfTwo(val) ? 1 : 0;
                update(fenwick, idx + 1,
                       newVal - currentVal);
                arr.set(idx, val);
            }
        }
        return results;
    }

    // Driver code
    public static void main(String[] args)
    {
        List<Integer> arr
            = new ArrayList<>(List.of(12, 11, 16, 2, 32));
        List<List<Integer> > queries
            = List.of(List.of(1, 0, 2), List.of(2, 4, 24),
                      List.of(1, 1, 4));
        List<Integer> result = handleQueries(arr, queries);
        for (int res : result) {
            System.out.print(res + " ");
        }
        System.out.println();
    }
}
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 tree if the element is a power of two
            update(fenwick, i + 1, 1, n)
    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
    # Utilize prefix sums for range sum
    return sum_query(fenwick, r + 1) - sum_query(fenwick, l)


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]
            # Check if current arr[idx] is a power of two
            current_val = 1 if is_power_of_two(arr[idx]) else 0
            # Check if new value 'val' is a power of two
            new_val = 1 if is_power_of_two(val) else 0
            # Update the Fenwick tree
            update(fenwick, idx + 1, new_val - current_val, n)
            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]
JavaScript
// Function to check if a number is a power of two using bitwise operation
const isPowerOfTwo = x => (x !== 0) && ((x & (x - 1)) === 0);

// Function to update a value in Fenwick Tree
const update = (fenwick, idx, value) => {
    while (idx < fenwick.length) {
        fenwick[idx] += value;
        idx += idx & -idx;
    }
}

// Function to build the Fenwick Tree from the given array
const buildFenwick = arr => {
    const n = arr.length;
    const fenwick = new Array(n + 1).fill(0);
    for (let i = 0; i < n; ++i) {
        if (isPowerOfTwo(arr[i])) {
            update(fenwick, i + 1, 1);
        }
    }
    return fenwick;
}

// Function to get the sum from index 1 to 'idx' in the Fenwick Tree
const sumQuery = (fenwick, idx) => {
    let sum = 0;
    while (idx > 0) {
        sum += fenwick[idx];
        idx -= idx & -idx;
    }
    return sum;
}

// Function to get the sum between indices 'l' and 'r' in the original array
const rangeQuery = (fenwick, l, r) => sumQuery(fenwick, r + 1) - sumQuery(fenwick, l);

// Function to handle queries and updates on the array
const handleQueries = (arr, queries) => {
    const fenwick = buildFenwick(arr);
    const results = [];

    for (const query of queries) {
        if (query[0] === 1) {
            const l = query[1], r = query[2];
            results.push(rangeQuery(fenwick, l, r));
        } else if (query[0] === 2) {
            const idx = query[1], val = query[2];
            const currentVal = isPowerOfTwo(arr[idx]) ? 1 : 0;
            const newVal = isPowerOfTwo(val) ? 1 : 0;
            update(fenwick, idx + 1, newVal - currentVal);
            arr[idx] = val;
        }
    }
    return results;
}

// Driver code
const arr = [12, 11, 16, 2, 32];
const queries = [[1, 0, 2], [2, 4, 24], [1, 1, 4]];
const result = handleQueries(arr, queries);
console.log(result.join(' '));

// This code is contributed by Akshita

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.




Next Article

Similar Reads