3495. Minimum Operations to Make Array Elements Zero
Problem Description
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.
How We Pick the Algorithm
Why Math / Bit Manipulation?
This problem maps to Math / Bit Manipulation through a short path in the full flowchart.
The problem involves counting operations where each division by 4 reduces a number, with the total work bounded by ceil(sum/2) and the max single-element cost.
Open in FlowchartIntuition
Let's start by understanding how many operations a single element needs. Each operation divides a number by 4 (taking the floor). So for an element x, the number of times we must divide it by 4 before it reaches 0 is the smallest integer p such that 4^p > x. For example, numbers in [1, 3] need 1 operation, numbers in [4, 15] need 2 operations, numbers in [16, 63] need 3 operations, and so on. In general, numbers in the range [4^(p-1), 4^p - 1] each require p operations.
Now here's the key twist: each operation processes two elements at once (we pick a and b and divide both). This means the total "work" to be done, measured as the sum of individual operations needed across all elements, can be shared two-at-a-time.
Let s be the total number of single-element divisions needed across all elements in [l, r]. Since each operation handles two divisions simultaneously, in the best case we could finish in about s / 2 operations, that is ceil(s / 2) operations. However, there's a constraint: a single element still needs its full count of operations performed on it one at a time. So if one element requires mx operations, we cannot finish in fewer than mx operations no matter how we pair things. Therefore the answer for a range is max(ceil(s / 2), mx).
Within the range [l, r], the largest element is r, and since the required operation count grows with the value, r needs the most operations. So mx equals the number of operations needed just for element r.
To compute s efficiently for many queries, we define f(x) as the sum of operation counts for all elements in [1, x]. We can build this by walking through each "bucket" [4^(p-1), 4^p - 1], counting how many elements fall in that bucket and multiplying by the per-element cost p. Then for any query, s = f(r) - f(l - 1), and the per-element count for r is mx = f(r) - f(r - 1). Summing max(ceil(s / 2), mx) over all queries gives the final answer.
Pattern Learn more about Math patterns.
Solution Approach
We use a prefix sum idea built on top of a closed-form helper function.
Step 1: Define the helper function f(x).
f(x) returns the sum of the minimum operations needed for every element in the range [1, x]. We compute it by iterating over the "buckets" of values that share the same operation count:
- We maintain
p, the starting value of the current bucket, beginning atp = 1(which is4^0). - We maintain
i, the per-element operation count for the current bucket, starting ati = 1. - The current bucket covers values
[p, 4*p - 1], but we clamp the upper end toxso we don't count beyondx. The number of elements in this bucket iscnt = min(p * 4 - 1, x) - p + 1. - We add
cnt * ito the running result, since each of thosecntelements costsioperations. - We then advance to the next bucket by multiplying
p *= 4and incrementingi += 1. - The loop continues while
p <= x.
def f(x: int) -> int:
res = 0
p = i = 1
while p <= x:
cnt = min(p * 4 - 1, x) - p + 1
res += cnt * i
i += 1
p *= 4
return res
Step 2: Process each query.
For each query [l, r]:
- Compute
s = f(r) - f(l - 1). This gives the total number of single-element divisions required across all elements in[l, r]. - Compute
mx = f(r) - f(r - 1). This isolates the operation count of just elementr, which is the largest element and therefore needs the most operations. - The answer for this query is
max((s + 1) // 2, mx), where(s + 1) // 2is the integer form ofceil(s / 2)(since two divisions can be handled per operation), andmxis the lower bound forced by the single hardest element.
We accumulate these values across all queries into ans and return it.
ans = 0
for l, r in queries:
s = f(r) - f(l - 1)
mx = f(r) - f(r - 1)
ans += max((s + 1) // 2, mx)
return ans
Complexity Analysis:
- Time complexity: Each call to
f(x)runs inO(log x)time because the bucket valuepquadruples each iteration. Withqqueries and a maximum valuer, the overall time isO(q * log r). - Space complexity:
O(1), as only a constant number of variables are used.
Example Walkthrough
Let's trace through a small example with queries = [[1, 5], [2, 4]].
Setup: Understanding f(x)
Recall f(x) sums the per-element operation counts for all values in [1, x]. The buckets are:
- Bucket
[1, 3]→ each element costs1operation - Bucket
[4, 15]→ each element costs2operations - Bucket
[16, 63]→ each element costs3operations
Let's precompute the values we'll need by walking the buckets.
Computing f(5):
| Iteration | p | i | Bucket [p, 4p-1] clamped to 5 | cnt = min(4p-1, 5) - p + 1 | Contribution cnt * i |
|---|---|---|---|---|---|
| 1 | 1 | 1 | [1, 3] | 3 - 1 + 1 = 3 | 3 * 1 = 3 |
| 2 | 4 | 2 | [4, 5] (clamped from [4,15]) | 5 - 4 + 1 = 2 | 2 * 2 = 4 |
Now p = 16 > 5, so we stop. f(5) = 3 + 4 = 7.
This makes sense: values 1, 2, 3 cost 1 each (3 total), and 4, 5 cost 2 each (4 total).
Computing the other needed values (using the same bucket logic):
f(0) = 0(empty range)f(1) = 1(just value1, costs1)f(3) = 3(values1,2,3, each costs1)f(4) = 3 + 2 = 5(values1,2,3cost1each, value4costs2)
Query 1: [1, 5]
The array is nums = [1, 2, 3, 4, 5]. Per-element costs are [1, 1, 1, 2, 2].
s = f(5) - f(0) = 7 - 0 = 7→ total single-element divisions neededmx = f(5) - f(4) = 7 - 5 = 2→ cost of the largest element5answer = max(ceil(7 / 2), 2) = max(4, 2) = 4
Why 4? We have 7 divisions to perform, and each operation handles 2 of them. That requires at least ceil(7/2) = 4 operations. The hardest single element (5) only needs 2, which isn't the binding constraint here. So 4 operations.
Query 2: [2, 4]
The array is nums = [2, 3, 4]. Per-element costs are [1, 1, 2].
s = f(4) - f(1) = 5 - 1 = 4→ total single-element divisions neededmx = f(4) - f(3) = 5 - 3 = 2→ cost of the largest element4answer = max(ceil(4 / 2), 2) = max(2, 2) = 2
Why 2? We have 4 divisions, handled 2 at a time → 2 operations. We can verify by hand: pair (4, 2) → (1, 0), then pair (3, 1) → (0, 0), and the 0s stay 0. That's exactly 2 operations, and all elements reach zero.
Final Result
Sum the per-query answers:
ans = 4 + 2 = 6
The walkthrough shows the two competing bounds in action: in Query 1 the shared-work bound ceil(s/2) dominates, while in Query 2 both bounds happen to tie. Taking max of the two guarantees we never undercount.
Solution Implementation
1from typing import List
2
3
4class Solution:
5 def minOperations(self, queries: List[List[int]]) -> int:
6 def total_operations(x: int) -> int:
7 """
8 Compute the sum of operations needed to reduce every integer
9 in the range [1, x] down to 0, where each integer n requires
10 floor(log4(n)) + 1 single-divisions.
11
12 Numbers in the range [4^(level-1), 4^level - 1] each need
13 'level' divisions to reach 0.
14 """
15 result = 0
16 power = level = 1 # power = 4^(level-1), level = number of ops for this band
17 while power <= x:
18 # Count how many integers in [power, 4*power - 1] are <= x
19 count = min(power * 4 - 1, x) - power + 1
20 result += count * level
21 level += 1
22 power *= 4
23 return result
24
25 answer = 0
26 for left, right in queries:
27 # Total single-division cost for all numbers in [left, right]
28 single_cost = total_operations(right) - total_operations(left - 1)
29 # Maximum operations any single number needs (the largest value, 'right')
30 max_cost = total_operations(right) - total_operations(right - 1)
31 # Each operation processes two numbers, so we need ceil(single_cost / 2),
32 # but never fewer than the worst single number's requirement.
33 answer += max((single_cost + 1) // 2, max_cost)
34 return answer
351class Solution {
2 /**
3 * For each query [l, r], we consider all integers in the range [l, r].
4 * Each integer x requires a certain number of operations to reduce it to 0,
5 * where each operation divides the number by 4 (the operation count equals
6 * floor(log4(x)) + 1, i.e., the "level" of x in powers of 4).
7 *
8 * Since one operation can act on two numbers at once, the minimum number of
9 * operations for a single query is max(ceil(totalSteps / 2), maxSteps),
10 * where totalSteps is the sum of steps over all numbers in [l, r], and
11 * maxSteps is the largest step count among numbers in [l, r].
12 */
13 public long minOperations(int[][] queries) {
14 long answer = 0;
15 for (int[] query : queries) {
16 int left = query[0], right = query[1];
17
18 // Total number of single-element reduction steps for the range [left, right].
19 long totalSteps = countSteps(right) - countSteps(left - 1);
20
21 // Maximum step count in the range, which equals the step count of the
22 // single largest number 'right' (since steps are non-decreasing with value).
23 long maxSteps = countSteps(right) - countSteps(right - 1);
24
25 // Each operation handles two numbers, so we need at least ceil(totalSteps / 2)
26 // operations; but we also need at least maxSteps operations to fully reduce
27 // the largest number.
28 answer += Math.max((totalSteps + 1) / 2, maxSteps);
29 }
30 return answer;
31 }
32
33 /**
34 * Computes the prefix sum of step counts for all integers in [1, x].
35 *
36 * A number n belongs to "level" i (1-indexed) if 4^(i-1) <= n <= 4^i - 1,
37 * meaning it takes i operations to reduce n to 0 by repeated division by 4.
38 *
39 * For each level i, this method counts how many numbers in [1, x] fall into
40 * that level and accumulates (count * i) into the result.
41 *
42 * @param x the upper bound of the range [1, x]
43 * @return the sum of step counts for all integers from 1 to x
44 */
45 private long countSteps(long x) {
46 long result = 0;
47 long power = 1; // power = 4^(i-1), the lower bound of the current level
48 int level = 1; // current level index (number of operations for this level)
49
50 while (power <= x) {
51 // Upper bound of the current level is (4^i - 1) = power * 4 - 1,
52 // clamped by x. Lower bound is 'power'.
53 long count = Math.min(power * 4 - 1, x) - power + 1;
54 result += count * level;
55
56 level++;
57 power *= 4;
58 }
59 return result;
60 }
61}
621class Solution {
2public:
3 long long minOperations(vector<vector<int>>& queries) {
4 // f(x) returns the total number of divide-by-4 operations required
5 // to reduce every integer in the range [1, x] down to 0.
6 // A value v needs (floor(log4(v)) + 1) operations.
7 // All values in [4^(i-1), 4^i - 1] share the same cost i.
8 auto f = [&](long long x) -> long long {
9 long long total = 0; // accumulated operation count
10 long long power = 1; // current block start, equals 4^(i-1)
11 int cost = 1; // number of operations for values in this block
12 while (power <= x) {
13 // Right boundary of the current block, clamped to x.
14 long long blockEnd = min(power * 4 - 1, x);
15 // Count of integers in this block within [1, x].
16 long long count = blockEnd - power + 1;
17 total += count * cost;
18 cost++;
19 power *= 4; // move to the next block: 4^i
20 }
21 return total;
22 };
23
24 long long ans = 0;
25 for (auto& query : queries) {
26 int left = query[0], right = query[1];
27
28 // Total operations needed for the whole range [left, right].
29 long long sum = f(right) - f(left - 1);
30
31 // Operations needed by the single largest element 'right'.
32 long long maxCost = f(right) - f(right - 1);
33
34 // Each operation processes two numbers, so we need at least
35 // ceil(sum / 2) operations; but also at least maxCost since one
36 // element cannot be sped up by pairing.
37 ans += max((sum + 1) / 2, maxCost);
38 }
39 return ans;
40 }
41};
421/**
2 * Computes the total number of operations required to reduce every integer
3 * in the range [1, x] down to zero, where each operation divides a value by 4
4 * (using floor division). A number n located in the bucket [4^(i-1), 4^i - 1]
5 * needs exactly i operations to reach zero.
6 *
7 * @param x - The upper bound of the range [1, x].
8 * @returns The total operation count for all integers in [1, x].
9 */
10const f = (x: number): number => {
11 let result = 0;
12 let power = 1; // Represents 4^(i-1), the lower bound of the current bucket.
13 let steps = 1; // Number of operations needed for numbers in the current bucket.
14
15 while (power <= x) {
16 // Count how many integers fall within the current bucket [power, 4*power - 1],
17 // capped by x so we never count beyond the requested range.
18 const count = Math.min(power * 4 - 1, x) - power + 1;
19 result += count * steps;
20 steps++;
21 power *= 4; // Move to the next bucket.
22 }
23
24 return result;
25};
26
27/**
28 * For each query [l, r], determines the minimum number of operations to reduce
29 * all integers in [l, r] to zero. Each operation can divide up to two numbers
30 * by 4 simultaneously, so the answer is bounded below by:
31 * - ceil(totalOperations / 2): because two numbers are handled per operation.
32 * - maxOperations: the hardest single number still needs its full step count.
33 *
34 * @param queries - A list of [l, r] ranges.
35 * @returns The accumulated minimum operations across all queries.
36 */
37function minOperations(queries: number[][]): number {
38 let ans = 0;
39
40 for (const [l, r] of queries) {
41 // Total operations needed for all integers in [l, r].
42 const sum = f(r) - f(l - 1);
43 // Operations needed for the single hardest number, which is r.
44 const max = f(r) - f(r - 1);
45 // Pair up operations (two per step) but never go below the hardest element.
46 ans += Math.max(Math.ceil(sum / 2), max);
47 }
48
49 return ans;
50}
51Time and Space Complexity
Time Complexity: O(q × log M)
The code processes each query in the queries list, where q is the number of queries. For each query [l, r], it performs the following operations:
- It calls the helper function
fthree times:f(r),f(l - 1),f(r - 1). - Inside
f(x), thewhileloop variablepstarts at1and is multiplied by4each iteration (p *= 4), continuing whilep <= x. This means the loop runs approximatelylog₄(x)times, which isO(log x).
Since x is bounded by the maximum value M in the query range, each call to f takes O(log M) time. With three calls per query and q queries total, the overall time complexity is O(q × log M).
Space Complexity: O(1)
The function f uses only a constant number of variables (res, p, i, cnt). The main loop uses the constant-space accumulator ans along with s and mx. No additional data structures that scale with input size are allocated, so the space complexity is O(1).
Pattern Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Using ceil(single_cost / 2) as the only answer and ignoring the single-element lower bound
The most tempting mistake is to reason that since each operation processes two numbers, the answer is simply ceil(single_cost / 2). This overlooks a critical constraint.
Why it's wrong:
An operation must select two distinct elements and divides both. Consider a query where one element (the largest, r) needs many divisions but every other element needs very few. You cannot "pair up" the hardest element with itself — each operation on it must also waste a division on some other element that may already be at zero. Once all the smaller elements hit zero, the hardest element still must be paired with a zero each step.
Example:
Take the range [1, 100]. The element 100 needs 4 divisions (100 → 25 → 6 → 1 → 0), while many small elements need only 1. Suppose single_cost is small enough that ceil(single_cost / 2) < 4. You would return a number smaller than 4, but it's physically impossible to finish in fewer steps than the 4 operations forced by element 100.
Solution: Always take the maximum of the two bounds:
answer += max((single_cost + 1) // 2, max_cost)
This max_cost = total_operations(right) - total_operations(right - 1) isolates the operation count of the single hardest element and enforces it as a floor.
Pitfall 2: Off-by-one errors in the prefix-sum subtraction
When computing the cost over [left, right], it's easy to write total_operations(right) - total_operations(left) instead of total_operations(left - 1).
Why it's wrong:
total_operations(left) already includes the cost of the element left, so subtracting it excludes left from the range, giving the cost of [left + 1, right]. Since the range is inclusive of left, you must subtract total_operations(left - 1).
Solution:
single_cost = total_operations(right) - total_operations(left - 1)
Note that total_operations(0) correctly returns 0 because the loop condition power <= x (i.e., 1 <= 0) is immediately false, so the left = 1 edge case is handled safely.
Pitfall 3: Incorrect integer ceiling computation
Writing single_cost // 2 (floor division) instead of (single_cost + 1) // 2 undercounts when single_cost is odd.
Why it's wrong:
If single_cost = 7, you genuinely need ceil(7 / 2) = 4 operations, but 7 // 2 = 3. The leftover single division still requires a full operation (paired with a zero element).
Solution: Use the standard integer-ceiling idiom:
(single_cost + 1) // 2 # equals ceil(single_cost / 2)
Avoid math.ceil(single_cost / 2), which converts to floating point and can produce rounding errors for very large single_cost values.
Pitfall 4: Miscomputing the bucket boundaries in total_operations
A subtle error is forgetting to clamp the bucket's upper bound to x:
count = power * 4 - 1 - power + 1 # WRONG: counts beyond x
Why it's wrong:
The final (highest) bucket usually extends past x. Without clamping, you count values larger than x that don't exist in the range, inflating the result.
Solution: Always clamp with min:
count = min(power * 4 - 1, x) - power + 1
This ensures the last partial bucket contributes only the elements that are actually ≤ x.
Ready to land your dream job?
Unlock your dream job with a 5-minute quiz for a personalized study roadmap!
Get My RoadmapWhich of these pictures shows the visit order of a depth-first search?

Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Want a Structured Path to Master System Design Too? Don’t Miss This!