Facebook Pixel

3344. Maximum Sized Array 🔒

MediumBit ManipulationBinary Search
Leetcode Link

Problem Description

You are given a positive integer s. You need to create a 3D array A with dimensions n × n × n, where each element is calculated using a specific formula.

For each element in the array, A[i][j][k] is defined as:

  • A[i][j][k] = i * (j OR k), where 0 <= i, j, k < n
  • The OR operation is the bitwise OR operation

Your task is to find the maximum possible value of n such that when you sum up all elements in the entire 3D array A, the total sum does not exceed s.

For example, if n = 2, you would have a 2×2×2 array with 8 elements total:

  • A[0][0][0] = 0 * (0 OR 0) = 0
  • A[0][0][1] = 0 * (0 OR 1) = 0
  • A[0][1][0] = 0 * (1 OR 0) = 0
  • A[0][1][1] = 0 * (1 OR 1) = 0
  • A[1][0][0] = 1 * (0 OR 0) = 0
  • A[1][0][1] = 1 * (0 OR 1) = 1
  • A[1][1][0] = 1 * (1 OR 0) = 1
  • A[1][1][1] = 1 * (1 OR 1) = 1

The sum would be 3, and if s >= 3, then n = 2 would be valid. You need to find the largest such n where the sum stays within the limit s.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Let's think about how to calculate the sum efficiently. If we expand the sum of all elements in the 3D array:

Sum = Σ(i=0 to n-1) Σ(j=0 to n-1) Σ(k=0 to n-1) i * (j OR k)

We can rearrange this as: Sum = Σ(i=0 to n-1) i * Σ(j=0 to n-1) Σ(k=0 to n-1) (j OR k)

Notice that the inner double summation Σ(j=0 to n-1) Σ(k=0 to n-1) (j OR k) is independent of i. Let's call this value S_or.

The key insight is that for any fixed value of j OR k, we can count how many (j, k) pairs produce that value. Due to symmetry in the OR operation, we can simplify our calculation.

For efficiency, we can precompute f[n] which represents Σ(i=0 to n-1) Σ(j=0 to i) (i OR j). Why up to i instead of n-1? Because (i OR j) where j <= i captures the essential pattern, and we can use symmetry to handle the full range.

The total sum for a given n can then be expressed as: Total Sum = f[n-1] * (n-1) * n / 2

This formula accounts for the multiplication by each i value and the proper counting of all (j, k) pairs.

Since the sum grows rapidly with n (approximately as n^5), and we want the maximum n where the sum doesn't exceed s, binary search becomes the natural choice. We can estimate that n won't exceed around 1330 based on the growth rate.

By preprocessing the values up to this maximum and then using binary search, we can efficiently find the largest valid n where the total sum stays within the limit s.

Learn more about Binary Search patterns.

Solution Approach

The solution consists of two main parts: preprocessing and binary search.

Preprocessing Phase:

First, we establish an upper bound for n. Since the sum grows approximately as (n-1)^5 / 4, and we need (n-1)^5 / 4 ≤ s, we can determine that n won't exceed approximately 1330 for reasonable values of s.

We precompute an array f where f[i] represents the sum Σ(j=0 to i) Σ(k=0 to j) (j OR k). The preprocessing code:

mx = 1330
f = [0] * mx
for i in range(1, mx):
    f[i] = f[i - 1] + i  # Add the case where j OR k = i (when j = k = i)
    for j in range(i):
        f[i] += 2 * (i | j)  # Count both (i, j) and (j, i) cases

The formula f[i] = f[i-1] + i + 2 * Σ(j=0 to i-1) (i OR j) builds upon the previous value and adds new contributions from index i.

Binary Search Phase:

Once we have the preprocessed values, we use binary search to find the maximum n such that the total sum doesn't exceed s.

For a given n, the total sum of the 3D array is calculated as: Total Sum = f[n-1] * (n-1) * n / 2

This formula accounts for:

  • f[n-1]: The sum pattern for OR operations
  • (n-1): Multiplication by each value of i from 0 to n-1
  • n / 2: Proper scaling factor for the 3D array structure

The binary search implementation:

def maxSizedArray(self, s: int) -> int:
    l, r = 1, mx
    while l < r:
        m = (l + r + 1) >> 1  # Middle point, biased towards upper half
        if f[m - 1] * (m - 1) * m // 2 <= s:
            l = m  # Sum is within limit, try larger n
        else:
            r = m - 1  # Sum exceeds limit, try smaller n
    return l

The binary search finds the largest n where the total sum remains ≤ s. The expression (l + r + 1) >> 1 ensures we round up when calculating the midpoint, preventing infinite loops when l and r are adjacent values.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through finding the maximum n for s = 10.

Step 1: Understanding the 3D Array

For n = 2, we calculate all elements:

  • When i = 0: All elements are 0 (since 0 * anything = 0)
  • When i = 1:
    • A[1][0][0] = 1 * (0 OR 0) = 1 * 0 = 0
    • A[1][0][1] = 1 * (0 OR 1) = 1 * 1 = 1
    • A[1][1][0] = 1 * (1 OR 0) = 1 * 1 = 1
    • A[1][1][1] = 1 * (1 OR 1) = 1 * 1 = 1

Total sum = 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 = 3

Step 2: Preprocessing Values

We build our f array:

  • f[0] = 0
  • f[1] = f[0] + 1 + 2*(1 OR 0) = 0 + 1 + 2*1 = 3
  • f[2] = f[1] + 2 + 2*(2 OR 0) + 2*(2 OR 1) = 3 + 2 + 2*2 + 2*3 = 3 + 2 + 4 + 6 = 15

Step 3: Binary Search

Now we search for maximum n where the sum ≤ 10:

Initial range: l = 1, r = 1330

First iteration:

  • m = (1 + 1330 + 1) / 2 = 666
  • Calculate sum for n = 666: This would be enormous, much greater than 10
  • Update: r = 665

After several iterations, we narrow down to:

  • When checking n = 2: Sum = f[1] * 1 * 2 / 2 = 3 * 1 * 1 = 3 ✓ (≤ 10)
  • When checking n = 3: Sum = f[2] * 2 * 3 / 2 = 15 * 2 * 3 / 2 = 45 ✗ (> 10)

Therefore, the maximum n = 2.

Verification: For n = 2, the total sum is 3, which is indeed ≤ 10. For n = 3, we would have a 3×3×3 array where the sum would be 45, exceeding our limit. Thus, n = 2 is the correct answer.

Solution Implementation

1# Maximum array size to precompute
2MAX_SIZE = 1330
3
4# Precompute cumulative OR sums for all array sizes
5# cumulative_or_sum[i] = sum of all (j | k) for 0 <= j, k < i
6cumulative_or_sum = [0] * MAX_SIZE
7
8for array_size in range(1, MAX_SIZE):
9    # Start with previous cumulative sum
10    cumulative_or_sum[array_size] = cumulative_or_sum[array_size - 1] + array_size
11  
12    # Add contribution of OR operations with the new element (array_size)
13    for element in range(array_size):
14        cumulative_or_sum[array_size] += 2 * (array_size | element)
15
16
17class Solution:
18    def maxSizedArray(self, s: int) -> int:
19        """
20        Find the maximum array size where the total cost doesn't exceed s.
21      
22        The cost for an array of size n is calculated as:
23        cumulative_or_sum[n-1] * (n-1) * n // 2
24      
25        Args:
26            s: Maximum allowed cost
27          
28        Returns:
29            Maximum array size that satisfies the cost constraint
30        """
31        left, right = 1, MAX_SIZE
32      
33        # Binary search for the maximum valid array size
34        while left < right:
35            # Calculate middle point (ceiling division)
36            mid = (left + right + 1) >> 1
37          
38            # Check if array of size 'mid' satisfies the cost constraint
39            cost = cumulative_or_sum[mid - 1] * (mid - 1) * mid // 2
40          
41            if cost <= s:
42                # Cost is within limit, try larger sizes
43                left = mid
44            else:
45                # Cost exceeds limit, try smaller sizes
46                right = mid - 1
47              
48        return left
49
1class Solution {
2    // Maximum array size to precompute
3    private static final int MAX_SIZE = 1330;
4  
5    // Precomputed values for each array size
6    // f[i] represents the sum: i + 2 * sum of (i | j) for all j from 0 to i-1
7    private static final long[] precomputedValues = new long[MAX_SIZE];
8  
9    // Static initialization block to precompute values
10    static {
11        for (int i = 1; i < MAX_SIZE; ++i) {
12            // Start with the previous computed value plus current index
13            precomputedValues[i] = precomputedValues[i - 1] + i;
14          
15            // Add twice the bitwise OR of current index with all previous indices
16            for (int j = 0; j < i; ++j) {
17                precomputedValues[i] += 2 * (i | j);
18            }
19        }
20    }
21  
22    public int maxSizedArray(long s) {
23        // Binary search boundaries
24        int left = 1;
25        int right = MAX_SIZE;
26      
27        // Binary search to find the maximum valid array size
28        while (left < right) {
29            // Calculate middle point (using bit shift for division by 2)
30            // Adding 1 before shifting ensures we round up
31            int mid = (left + right + 1) >> 1;
32          
33            // Check if the computed value for size (mid-1) satisfies the constraint
34            // The formula multiplies precomputed value by (mid-1) * mid / 2
35            if (precomputedValues[mid - 1] * (mid - 1) * mid / 2 <= s) {
36                // If condition holds, search in the upper half
37                left = mid;
38            } else {
39                // Otherwise, search in the lower half
40                right = mid - 1;
41            }
42        }
43      
44        // Return the maximum array size that satisfies the constraint
45        return left;
46    }
47}
48
1// Maximum array size constant
2const int MAX_SIZE = 1330;
3
4// Precomputed values for array size calculations
5// f[i] represents the sum of i plus all pairwise OR operations for elements 0 to i-1
6long long cumulativeOrSum[MAX_SIZE];
7
8// Initialize the precomputed values at compile time
9auto initializer = [] {
10    cumulativeOrSum[0] = 0;
11  
12    for (int i = 1; i < MAX_SIZE; ++i) {
13        // Start with the sum from previous iteration plus current index
14        cumulativeOrSum[i] = cumulativeOrSum[i - 1] + i;
15      
16        // Add twice the bitwise OR of current index with all previous indices
17        // (twice because each pair contributes to both elements)
18        for (int j = 0; j < i; ++j) {
19            cumulativeOrSum[i] += 2 * (i | j);
20        }
21    }
22    return 0;
23}();
24
25class Solution {
26public:
27    /**
28     * Finds the maximum size of an array that satisfies the given constraint.
29     * Uses binary search to find the largest valid array size.
30     * 
31     * @param s The upper bound constraint value
32     * @return The maximum size of the array that satisfies the constraint
33     */
34    int maxSizedArray(long long s) {
35        int left = 1;
36        int right = MAX_SIZE;
37      
38        // Binary search for the maximum valid array size
39        while (left < right) {
40            // Calculate middle point (ceiling division to avoid infinite loop)
41            int mid = (left + right + 1) >> 1;
42          
43            // Check if array of size 'mid' satisfies the constraint
44            // The formula calculates: f[mid-1] * (mid-1) * mid / 2
45            if (cumulativeOrSum[mid - 1] * (mid - 1) * mid / 2 <= s) {
46                // Valid size, try to find a larger one
47                left = mid;
48            } else {
49                // Size too large, search in smaller range
50                right = mid - 1;
51            }
52        }
53      
54        return left;
55    }
56};
57
1// Maximum array size constant
2const MAX_SIZE = 1330;
3
4// Precomputed values array for optimization
5// f[i] represents the cumulative sum for size i
6const precomputedValues: bigint[] = Array(MAX_SIZE).fill(0n);
7
8// Initialize precomputed values using IIFE (Immediately Invoked Function Expression)
9(() => {
10    // Base case: empty array has sum 0
11    precomputedValues[0] = 0n;
12  
13    // Calculate cumulative sums for each array size
14    for (let i = 1; i < MAX_SIZE; i++) {
15        // Start with previous sum plus current index
16        precomputedValues[i] = precomputedValues[i - 1] + BigInt(i);
17      
18        // Add contribution from bitwise OR operations with all previous indices
19        for (let j = 0; j < i; j++) {
20            // Each OR operation contributes twice (for both i|j and j|i pairs)
21            precomputedValues[i] += BigInt(2) * BigInt(i | j);
22        }
23    }
24})();
25
26/**
27 * Finds the maximum sized array whose total sum does not exceed the given limit
28 * Uses binary search to find the largest valid array size
29 * 
30 * @param s - The maximum allowed sum
31 * @returns The maximum array size that satisfies the sum constraint
32 */
33function maxSizedArray(s: number): number {
34    // Binary search boundaries
35    let left = 1;
36    let right = MAX_SIZE;
37    const targetSum = BigInt(s);
38
39    // Binary search for the maximum valid array size
40    while (left < right) {
41        // Calculate middle point (ceiling division for upper bound)
42        const mid = (left + right + 1) >> 1;
43      
44        // Calculate the total sum for an array of size mid
45        // Formula: (precomputedValues[mid-1] * (mid-1) * mid) / 2
46        const currentSum = (precomputedValues[mid - 1] * BigInt(mid - 1) * BigInt(mid)) / BigInt(2);
47      
48        if (currentSum <= targetSum) {
49            // Current size is valid, try larger sizes
50            left = mid;
51        } else {
52            // Current size exceeds limit, try smaller sizes
53            right = mid - 1;
54        }
55    }
56  
57    return left;
58}
59

Time and Space Complexity

The time complexity consists of two parts:

  1. Preprocessing phase: The outer loop runs from 1 to mx-1 (i.e., O(mx) iterations). For each iteration i, the inner loop runs i times, performing a bitwise OR operation and arithmetic operations. This results in 1 + 2 + 3 + ... + (mx-1) = (mx-1) * mx / 2 operations, giving us O(mx^2) time complexity for preprocessing.

  2. Binary search phase: The maxSizedArray method performs a binary search between 1 and mx. Each iteration of the while loop reduces the search space by half, resulting in O(log mx) time complexity.

Therefore, the total time complexity is O(mx^2 + log mx). Since mx = 1330 is a constant, we can also express this as O(n^2 + log n) where n represents the upper bound of the search range.

The space complexity is O(mx) or O(n), as we allocate an array f of size mx to store the precomputed values. No additional space that scales with input is used during the binary search phase.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Overflow in Cost Calculation

Pitfall: The most critical issue in this solution is potential integer overflow when calculating the cost:

cost = cumulative_or_sum[mid - 1] * (mid - 1) * mid // 2

For large values of mid (approaching 1330), the multiplication cumulative_or_sum[mid - 1] * (mid - 1) * mid can exceed Python's integer limits in other languages or cause performance issues even in Python with very large numbers.

Solution: Rearrange the calculation to perform division earlier or check for overflow:

# Option 1: Divide earlier to reduce intermediate values
cost = cumulative_or_sum[mid - 1] * ((mid - 1) * mid // 2)

# Option 2: Check if multiplication would exceed s before computing
if cumulative_or_sum[mid - 1] > s * 2 // ((mid - 1) * mid):
    right = mid - 1
    continue

2. Off-by-One Error in Binary Search

Pitfall: The binary search uses (left + right + 1) >> 1 for the midpoint calculation. If you accidentally use (left + right) >> 1 instead, the binary search may enter an infinite loop when left = right - 1 and the condition holds true for mid = left.

Solution: Always use ceiling division when the update rule is left = mid:

mid = (left + right + 1) >> 1  # Correct: ceiling division
# NOT: mid = (left + right) >> 1  # Wrong: can cause infinite loop

3. Incorrect Precomputation Formula

Pitfall: The precomputation loop might be implemented incorrectly:

# Wrong: Missing the factor of 2 for symmetric pairs
for array_size in range(1, MAX_SIZE):
    cumulative_or_sum[array_size] = cumulative_or_sum[array_size - 1] + array_size
    for element in range(array_size):
        cumulative_or_sum[array_size] += (array_size | element)  # Missing factor of 2!

This misses that for each unique OR value, we need to count both (j, k) and (k, j) pairs when j ≠ k.

Solution: Ensure the factor of 2 is included for non-diagonal elements:

for array_size in range(1, MAX_SIZE):
    cumulative_or_sum[array_size] = cumulative_or_sum[array_size - 1] + array_size
    for element in range(array_size):
        cumulative_or_sum[array_size] += 2 * (array_size | element)  # Correct

4. Hardcoded Maximum Size Without Validation

Pitfall: Using a fixed MAX_SIZE = 1330 without considering the actual input range. If s is extremely large (beyond what 1330 can handle), the solution would return an incorrect answer.

Solution: Dynamically determine the upper bound based on s:

def maxSizedArray(self, s: int) -> int:
    # Dynamically calculate upper bound
    # Since sum ~ n^5/4, we have n ~ (4*s)^(1/5)
    import math
    max_possible = min(MAX_SIZE, int((4 * s) ** 0.2) + 10)  # Add buffer
  
    left, right = 1, max_possible
    # ... rest of binary search

5. Not Handling Edge Cases

Pitfall: Not properly handling small values of s:

  • When s = 0, the answer should be 1 (array of size 1 has sum 0)
  • When s < 0, the input is invalid

Solution: Add edge case handling:

def maxSizedArray(self, s: int) -> int:
    if s == 0:
        return 1
    if s < 0:
        raise ValueError("s must be non-negative")
  
    # Continue with normal binary search...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Consider the classic dynamic programming of longest increasing subsequence:

Find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.

For example, the length of LIS for [50, 3, 10, 7, 40, 80] is 4 and LIS is [3, 7, 40, 80].

What is the recurrence relation?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More