Minimum Operations to Make Array Elements Zero
Problem
You are given a 2D array queries, where queries[i] is of the form [l, r]. Each queries[i] defines an array of integers nums consisting of elements ranging from l to r, both inclusive.
In one operation, you can:
- Select two integers
aandbfrom the array. - Replace them with
floor(a / 4)andfloor(b / 4).
Your task is to determine the minimum number of operations required to reduce all elements of the array to zero for each query. Return the sum of the results for all queries.
Examples
Example 1
Input: queries = [[1,2],[2,4]]
Output: 3
Explanation:
For `queries[0]`:
* The initial array is `nums = [1, 2]`.
* In the first operation, select `nums[0]` and `nums[1]`. The array becomes `[0, 0]`.
* The minimum number of operations required is 1.
For `queries[1]`:
* The initial array is `nums = [2, 3, 4]`.
* In the first operation, select `nums[0]` and `nums[2]`. The array becomes `[0, 3, 1]`.
* In the second operation, select `nums[1]` and `nums[2]`. The array becomes `[0, 0, 0]`.
* The minimum number of operations required is 2.
The output is `1 + 2 = 3`.
Example 2
Input: queries = [[2,6]]
Output: 4
Explanation:
For `queries[0]`:
* The initial array is `nums = [2, 3, 4, 5, 6]`.
* In the first operation, select `nums[0]` and `nums[3]`. The array becomes `[0, 3, 4, 1, 6]`.
* In the second operation, select `nums[2]` and `nums[4]`. The array becomes `[0, 3, 1, 1, 1]`.
* In the third operation, select `nums[1]` and `nums[2]`. The array becomes `[0, 0, 0, 1, 1]`.
* In the fourth operation, select `nums[3]` and `nums[4]`. The array becomes `[0, 0, 0, 0, 0]`.
* The minimum number of operations required is 4.
The output is 4.
Constraints
1 <= queries.length <= 10^5queries[i].length == 2queries[i] == [l, r]1 <= l < r <= 10^9
Solution
Method 1 – Greedy & Bit Manipulation
Intuition
Each operation reduces two numbers by dividing them by 4 (floor). The process is similar to reducing the largest numbers first, and the minimum number of operations is determined by the maximum number of times you need to divide any number in the range by 4 until it reaches zero. Since you can pair up numbers, the answer for each query is the ceiling of half the sum of the number of divisions needed for each number.
Approach
- For each query [l, r], count the number of times each number in [l, r] needs to be divided by 4 to reach zero.
- For each number x in [l, r], count the number of times you can divide x by 4 before it becomes zero.
- Sum up all these counts for the range.
- Since each operation can process two numbers, the minimum number of operations is ceil(total_count / 2).
- For large ranges, use math to compute the sum efficiently by counting how many numbers have a given number of divisions.
Code
C++
class Solution {
public:
long long minOperations(std::vector<std::vector<int>>& queries) {
long long ans = 0;
for (auto& q : queries) {
int l = q[0], r = q[1];
long long cnt = 0;
for (int x = l; x <= r; ++x) {
int t = x;
while (t) {
t /= 4;
++cnt;
}
}
ans += (cnt + 1) / 2;
}
return ans;
}
};
Go
func minOperations(queries [][]int) int64 {
var ans int64 = 0
for _, q := range queries {
l, r := q[0], q[1]
var cnt int64 = 0
for x := l; x <= r; x++ {
t := x
for t > 0 {
t /= 4
cnt++
}
}
ans += (cnt + 1) / 2
}
return ans
}
Java
class Solution {
public long minOperations(int[][] queries) {
long ans = 0;
for (int[] q : queries) {
int l = q[0], r = q[1];
long cnt = 0;
for (int x = l; x <= r; ++x) {
int t = x;
while (t > 0) {
t /= 4;
cnt++;
}
}
ans += (cnt + 1) / 2;
}
return ans;
}
}
Kotlin
class Solution {
fun minOperations(queries: Array<IntArray>): Long {
var ans = 0L
for (q in queries) {
val l = q[0]
val r = q[1]
var cnt = 0L
for (x in l..r) {
var t = x
while (t > 0) {
t /= 4
cnt++
}
}
ans += (cnt + 1) / 2
}
return ans
}
}
Python
from typing import List
class Solution:
def minOperations(self, queries: List[List[int]]) -> int:
ans: int = 0
for l, r in queries:
cnt: int = 0
for x in range(l, r + 1):
t: int = x
while t:
t //= 4
cnt += 1
ans += (cnt + 1) // 2
return ans
Rust
impl Solution {
pub fn min_operations(queries: Vec<Vec<i32>>) -> i64 {
let mut ans: i64 = 0;
for q in queries {
let (l, r) = (q[0], q[1]);
let mut cnt: i64 = 0;
for x in l..=r {
let mut t = x;
while t > 0 {
t /= 4;
cnt += 1;
}
}
ans += (cnt + 1) / 2;
}
ans
}
}
TypeScript
class Solution {
minOperations(queries: number[][]): number {
let ans: number = 0;
for (const [l, r] of queries) {
let cnt: number = 0;
for (let x = l; x <= r; ++x) {
let t: number = x;
while (t > 0) {
t = Math.floor(t / 4);
cnt++;
}
}
ans += Math.floor((cnt + 1) / 2);
}
return ans;
}
}
Complexity
- ⏰ Time complexity:
O(Q * N * logM)— For each query, for each number in the range, we divide by 4 until zero. Q = number of queries, N = range size, M = max number value. - 🧺 Space complexity:
O(1)— Only a few variables are used, no extra space proportional to input size.
Method 2 – Interval Grouping & Logarithmic Counting
Intuition
To reduce a number n to zero by repeated division by 4, we need floor(log₄(n)) + 1 operations. This means we can group numbers by the number of divisions required:
- Numbers in
[1, 3](i.e., from4⁰to4¹ - 1) require 1 division. - Numbers in
[4, 15](i.e., from4¹to4² - 1) require 2 divisions. - Numbers in
[16, 63](i.e., from4²to4³ - 1) require 3 divisions. - ... and so on.
This observation allows us to efficiently count the total number of operations needed for any interval.
Approach
- For each query
[l, r], iterate over division levelsdfrom 1 up to 16 (since4¹⁶ > 1e9). - For each level, define the interval
[prev, cur - 1]whereprev = 4^(d-1)andcur = 4^d. - Find the overlap between
[l, r]and[prev, cur - 1]. - For each overlapping number, add
dto the total operations. - After processing all intervals, since each operation can process two numbers, return
ceil(total_operations / 2)for the query. - Sum the results for all queries.
Code
C++
class Solution {
public:
long long minOperations(std::vector<std::vector<int>>& queries) {
long long ans = 0;
for (auto& q : queries) {
long long l = q[0], r = q[1], ops = 0;
long long prev = 1;
for (int d = 1; d < 17; ++d) {
long long cur = prev * 4LL;
long long left = std::max(l, prev), right = std::min(r, cur - 1);
if (right >= left) ops += (right - left + 1) * d;
prev = cur;
}
ans += (ops + 1) / 2;
}
return ans;
}
};
Go
func minOperations(queries [][]int) int64 {
var ans int64 = 0
for _, q := range queries {
l, r := int64(q[0]), int64(q[1])
var ops int64 = 0
prev := int64(1)
for d := int64(1); d < 17; d++ {
cur := prev * 4
left := max(l, prev)
right := min(r, cur-1)
if right >= left {
ops += (right - left + 1) * d
}
prev = cur
}
ans += (ops + 1) / 2
}
return ans
}
func max(a, b int64) int64 { if a > b { return a }; return b }
func min(a, b int64) int64 { if a < b { return a }; return b }
Java
class Solution {
public long minOperations(int[][] queries) {
long ans = 0;
for (int[] q : queries) {
long l = q[0], r = q[1], ops = 0;
long prev = 1;
for (int d = 1; d < 17; d++) {
long cur = prev * 4L;
long left = Math.max(l, prev), right = Math.min(r, cur - 1);
if (right >= left) ops += (right - left + 1) * d;
prev = cur;
}
ans += (ops + 1) / 2;
}
return ans;
}
}
Kotlin
class Solution {
fun minOperations(queries: Array<IntArray>): Long {
var ans = 0L
for (q in queries) {
var l = q[0].toLong()
var r = q[1].toLong()
var ops = 0L
var prev = 1L
for (d in 1..16) {
val cur = prev * 4L
val left = maxOf(l, prev)
val right = minOf(r, cur - 1)
if (right >= left) ops += (right - left + 1) * d
prev = cur
}
ans += (ops + 1) / 2
}
return ans
}
}
Python
from typing import List
class Solution:
def minOperations(self, queries: List[List[int]]) -> int:
ans = 0
for l, r in queries:
ops = 0
prev = 1
for d in range(1, 17):
cur = prev * 4
left = max(l, prev)
right = min(r, cur - 1)
if right >= left:
ops += (right - left + 1) * d
prev = cur
ans += (ops + 1) // 2
return ans
Rust
impl Solution {
pub fn min_operations(queries: Vec<Vec<i32>>) -> i64 {
let mut ans: i64 = 0;
for q in queries {
let l = q[0] as i64;
let r = q[1] as i64;
let mut ops: i64 = 0;
let mut prev: i64 = 1;
for d in 1..17 {
let cur = prev * 4;
let left = std::cmp::max(l, prev);
let right = std::cmp::min(r, cur - 1);
if right >= left {
ops += (right - left + 1) * d;
}
prev = cur;
}
ans += (ops + 1) / 2;
}
ans
}
}
TypeScript
class Solution {
minOperations(queries: number[][]): number {
let ans: number = 0;
for (const [l, r] of queries) {
let ops: number = 0;
let prev: number = 1;
for (let d = 1; d < 17; d++) {
const cur = prev * 4;
const left = Math.max(l, prev);
const right = Math.min(r, cur - 1);
if (right >= left) ops += (right - left + 1) * d;
prev = cur;
}
ans += Math.floor((ops + 1) / 2);
}
return ans;
}
}
Complexity
- ⏰ Time complexity:
O(Q * logM)— For each query, we process up to 16 intervals (since4^16 > 1e9). - 🧺 Space complexity:
O(1)— Only a few variables are used, no extra space proportional to input size.