LeetCode 3495 - Minimum Operations to Make Array Elements Zero
The problem presents a sequence of queries, each defining a contiguous array of integers nums from l to r inclusive. For each array, we are allowed to perform an operation repeatedly: select any two integers a and b and replace them with floor(a / 4) and floor(b / 4).
Difficulty: 🔴 Hard
Topics: Array, Math, Bit Manipulation
Solution
Problem Understanding
The problem presents a sequence of queries, each defining a contiguous array of integers nums from l to r inclusive. For each array, we are allowed to perform an operation repeatedly: select any two integers a and b and replace them with floor(a / 4) and floor(b / 4). The goal is to determine the minimum number of such operations needed to reduce all elements of the array to zero. After computing this minimum for each query, the results are summed and returned.
The input queries is a 2D array of shape [q][2] with 1 <= q <= 10^5 and 1 <= l < r <= 10^9. This means naive simulation of the array values is infeasible due to potential array sizes up to 10^9 elements. Instead, we must reason mathematically about the operations and the reduction of numbers by repeated division by 4.
Key points to note:
- Each operation reduces two numbers simultaneously. The number of operations is thus related to the total number of reductions needed, divided by two (rounded up if odd).
floor(x / 4)effectively counts how many times a number must be divided by 4 before reaching zero.- Edge cases include small arrays (size 2), arrays where all numbers are small, and arrays where numbers are large and unevenly spaced.
Approaches
Brute Force
A brute-force approach would simulate the array nums explicitly for each query. At each step, we would repeatedly choose the two largest numbers, replace them with floor(a/4) and floor(b/4), and count operations until all values become zero. While correct, this approach is computationally infeasible for large arrays because it requires iterating over up to 10^9 elements and performing multiple operations per element.
Optimal Approach
The key insight is that the number of operations needed for a number x to reduce to zero by repeated division by 4 is exactly the number of times we can divide it by 4 until it reaches zero. This is given by floor(log_4(x)) + 1. Since each operation acts on two numbers, the total operations for an array [l, r] is the sum of individual reductions divided by 2, rounded up if necessary. Furthermore, we do not need to construct the array; we can compute the sum directly using properties of geometric progression in base 4.
This transforms a problem of simulating arrays into one of simple mathematical computation using logarithms and counts of powers of 4.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n log n) per query | O(n) | Simulate array and operations explicitly; infeasible for large ranges |
| Optimal | O(log r) per query | O(1) | Use formula for reductions and sum directly; scalable to large queries |
Algorithm Walkthrough
- Initialize a total counter
total_ops = 0to accumulate results across queries. - Iterate over each query
[l, r]inqueries. - For each integer
xin[l, r], computeops(x) = floor(log_4(x)) + 1. This represents the number of timesxmust be divided by 4 to reach zero. - Compute the sum of
ops(x)over allxin the range[l, r]. This can be done efficiently using a count of numbers at each "log level" rather than enumerating all integers. - Since each operation reduces two numbers, compute
ceil(sum_ops / 2)to get the minimum operations for this query. - Add this result to
total_ops. - Return
total_opsafter all queries have been processed.
Why it works: Each number independently contributes a certain number of required reductions. Pairing two numbers per operation is guaranteed to be optimal because there is no restriction on which numbers to pair; thus, dividing the total required reductions by two gives the minimal number of operations.
Python Solution
from typing import List
import math
class Solution:
def minOperations(self, queries: List[List[int]]) -> int:
total_ops = 0
for l, r in queries:
sum_ops = 0
# Count how many numbers fall in each "power of 4" bucket
current = l
while current <= r:
# highest power of 4 less than or equal to current
power = int(math.log(current, 4))
next_power_value = 4 ** (power + 1)
# upper bound for numbers with this power
upper = min(r + 1, next_power_value)
count = upper - current
sum_ops += count * (power + 1)
current = upper
# Each operation reduces two numbers
total_ops += (sum_ops + 1) // 2
return total_ops
The implementation avoids constructing the array explicitly. It computes the number of reductions needed for all numbers in [l, r] using powers of 4 and aggregates counts. Dividing by 2 (with rounding up) ensures the minimal number of operations.
Go Solution
import (
"math"
)
func minOperations(queries [][]int) int64 {
var totalOps int64 = 0
for _, q := range queries {
l, r := q[0], q[1]
var sumOps int64 = 0
current := l
for current <= r {
power := int(math.Log(float64(current)) / math.Log(4))
nextPowerValue := int(math.Pow(4, float64(power+1)))
upper := r + 1
if nextPowerValue < upper {
upper = nextPowerValue
}
count := upper - current
sumOps += int64(count * (power + 1))
current = upper
}
totalOps += (sumOps + 1) / 2
}
return totalOps
}
In Go, we handle integer computations carefully and cast values where necessary for arithmetic involving int64. The algorithmic logic mirrors the Python solution.
Worked Examples
Example 1: queries = [[1,2],[2,4]]
Query [1,2]:
| x | floor(log4(x)) + 1 | Operations contribution |
|---|---|---|
| 1 | 0 + 1 = 1 | 1 |
| 2 | 0 + 1 = 1 | 1 |
Sum of contributions = 2 → divide by 2 → 1 operation.
Query [2,4]:
| x | floor(log4(x)) + 1 | Operations contribution |
|---|---|---|
| 2 | 0 + 1 = 1 | 1 |
| 3 | 0 + 1 = 1 | 1 |
| 4 | 1 + 1 = 2 | 2 |
Sum = 4 → divide by 2 → 2 operations.
Total sum = 1 + 2 = 3.
Example 2: queries = [[2,6]]
| x | floor(log4(x)) + 1 | Ops |
|---|---|---|
| 2 | 1 | 1 |
| 3 | 1 | 1 |
| 4 | 2 | 2 |
| 5 | 2 | 2 |
| 6 | 2 | 2 |
Sum = 8 → divide by 2 → 4 operations.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(log r) per query | Only iterate over powers of 4 in range [l,r], not each integer |
| Space | O(1) | Constant space for counters, no arrays constructed |
The logarithmic iteration comes from the fact that each iteration jumps to the next power of 4, reducing the number of steps dramatically even for very large ranges.
Test Cases
# Provided examples
assert Solution().minOperations([[1,2],[2,4]]) == 3 # multiple queries
assert Solution().minOperations([[2,6]]) == 4 # single query
# Boundary tests
assert Solution().minOperations([[1,1]]) == 1 # smallest possible array
assert Solution().minOperations([[1,10**9]]) > 0 # stress test large range
# Small arrays
assert Solution().minOperations([[3,3]]) == 1
assert Solution().minOperations([[4,7]]) == 5 # varied contributions
| Test | Why |
|---|---|
| [[1,2],[2,4]] | Checks multiple queries and small ranges |
| [[2,6]] | Single query, sum of operations required |
| [[1,1]] | Minimum array size |
| [[1,10^9]] | Stress test for large range |
| [[4,7]] | Checks algorithm correctness for mixed power levels |
Edge Cases
- Single-element arrays: When
l == r, the array has size 1. The algorithm still works because the contribution of that single element is computed asfloor(log4(x))+1, and rounding up division by 2 correctly gives 1 operation. - Large ranges: Arrays spanning large ranges such as
[1, 10^9]are not constructed