Facebook Pixel

2513. Minimize the Maximum of Two Arrays

Problem Description

You need to create two arrays arr1 and arr2 by adding positive integers to them according to specific rules:

Rules for arr1:

  • Must contain exactly uniqueCnt1 distinct positive integers
  • Each integer in arr1 must NOT be divisible by divisor1

Rules for arr2:

  • Must contain exactly uniqueCnt2 distinct positive integers
  • Each integer in arr2 must NOT be divisible by divisor2

Additional constraint:

  • The two arrays must be completely disjoint (no integer can appear in both arrays)

Given the parameters divisor1, divisor2, uniqueCnt1, and uniqueCnt2, you need to find the minimum possible value of the maximum integer across both arrays.

In other words, if you optimally choose which positive integers to place in each array while satisfying all constraints, what's the smallest possible value for the largest number you'll need to use?

Example to clarify: If divisor1 = 2, divisor2 = 3, uniqueCnt1 = 1, uniqueCnt2 = 1:

  • arr1 needs 1 number not divisible by 2 (could use 1, 3, 5, 7, ...)
  • arr2 needs 1 number not divisible by 3 (could use 1, 2, 4, 5, ...)
  • Since arrays can't share numbers, one optimal solution is arr1 = [1] and arr2 = [2]
  • The maximum value used is 2, which would be the answer
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that if we have a maximum value x, we can determine whether it's possible to construct valid arrays with all numbers ≤ x.

Think about what numbers are available up to x:

  • For arr1: We can use all positive integers up to x that are NOT divisible by divisor1
  • For arr2: We can use all positive integers up to x that are NOT divisible by divisor2

How many such numbers exist? If we have x total numbers:

  • Numbers divisible by divisor1: x // divisor1
  • Numbers NOT divisible by divisor1: x - (x // divisor1)
  • Similarly, numbers NOT divisible by divisor2: x - (x // divisor2)

But there's a catch - some numbers might be valid for both arrays (not divisible by either divisor). Since we need disjoint arrays, we must carefully distribute these "flexible" numbers.

The critical observation is that numbers divisible by both divisor1 and divisor2 (i.e., divisible by lcm(divisor1, divisor2)) can't go in either array. So the total available numbers for both arrays combined is x minus the count of numbers divisible by the LCM.

This transforms our problem into a binary search question: "What's the smallest x such that we can fill both arrays?" We can check if a given x works by:

  1. Counting how many valid numbers exist for arr1 only
  2. Counting how many valid numbers exist for arr2 only
  3. Counting how many numbers are valid for either array (flexible numbers)
  4. Verifying we have enough numbers to satisfy both uniqueCnt1 and uniqueCnt2

Since we want the minimum possible maximum, binary search naturally finds the smallest x where this becomes feasible.

Learn more about Math and Binary Search patterns.

Solution Approach

The solution uses binary search to find the minimum possible maximum value. Here's how the implementation works:

Binary Search Setup: We use bisect_left to search for the smallest value x where our condition function f(x) returns True. The search range is set to range(10**10) which is large enough to cover all practical cases.

The Check Function f(x): For a given maximum value x, we calculate:

  1. Count of valid numbers for arr1:

    • cnt1 = x // divisor1 * (divisor1 - 1) + x % divisor1
    • This formula counts numbers from 1 to x that are NOT divisible by divisor1
    • For every complete group of divisor1 numbers, we have (divisor1 - 1) valid ones
    • Plus any remaining numbers in the incomplete group (all are valid since they're less than divisor1)
  2. Count of valid numbers for arr2:

    • cnt2 = x // divisor2 * (divisor2 - 1) + x % divisor2
    • Same logic as above but for divisor2
  3. Count of numbers valid for either array:

    • divisor = lcm(divisor1, divisor2) (calculated once outside the function)
    • cnt = x // divisor * (divisor - 1) + x % divisor
    • This counts numbers NOT divisible by the LCM (numbers that could potentially go in either array)

Validation Conditions: The function returns True if all three conditions are met:

  • cnt1 >= uniqueCnt1: Enough valid numbers exist for arr1
  • cnt2 >= uniqueCnt2: Enough valid numbers exist for arr2
  • cnt >= uniqueCnt1 + uniqueCnt2: Total available numbers (excluding those divisible by LCM) is sufficient for both arrays combined

The third condition is crucial - it ensures we have enough distinct numbers to fill both arrays without overlap, accounting for numbers that must go to specific arrays (those divisible by one divisor but not the other).

The binary search efficiently finds the smallest x where all conditions are satisfied, giving us the minimum possible maximum value.

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 a concrete example with divisor1 = 2, divisor2 = 3, uniqueCnt1 = 3, uniqueCnt2 = 2.

We need:

  • arr1: 3 distinct numbers not divisible by 2 (odd numbers)
  • arr2: 2 distinct numbers not divisible by 3
  • The arrays must be disjoint

Binary Search Process:

Let's check if x = 7 works:

  1. Count valid numbers for arr1 (not divisible by 2):

    • Numbers 1-7: {1, 2, 3, 4, 5, 6, 7}
    • Not divisible by 2: {1, 3, 5, 7} → 4 numbers
    • Using formula: 7 // 2 * (2-1) + 7 % 2 = 3 * 1 + 1 = 4
  2. Count valid numbers for arr2 (not divisible by 3):

    • Not divisible by 3: {1, 2, 4, 5, 7} → 5 numbers
    • Using formula: 7 // 3 * (3-1) + 7 % 3 = 2 * 2 + 1 = 5
  3. Count total available (not divisible by lcm(2,3) = 6):

    • Not divisible by 6: {1, 2, 3, 4, 5, 7} → 6 numbers
    • Using formula: 7 // 6 * (6-1) + 7 % 6 = 1 * 5 + 1 = 6

Validation:

  • cnt1 = 4 >= 3 ✓ (enough odd numbers for arr1)
  • cnt2 = 5 >= 2 ✓ (enough non-multiples of 3 for arr2)
  • cnt = 6 >= 3 + 2 = 5 ✓ (enough total distinct numbers)

All conditions met! But let's verify x = 6 doesn't work:

For x = 6:

  • Valid for arr1: {1, 3, 5} → 3 numbers
  • Valid for arr2: {1, 2, 4, 5} → 4 numbers
  • Total available: {1, 2, 3, 4, 5} → 5 numbers (6 is divisible by lcm=6)

Conditions: 3 >= 3 ✓, 4 >= 2 ✓, 5 >= 5

Actually x = 6 works! Let's check x = 5:

  • Valid for arr1: {1, 3, 5} → 3 numbers
  • Valid for arr2: {1, 2, 4, 5} → 4 numbers
  • Total available: {1, 2, 3, 4, 5} → 5 numbers

Conditions work, so let's try x = 4:

  • Valid for arr1: {1, 3} → 2 numbers
  • Valid for arr2: {1, 2, 4} → 3 numbers
  • Total available: {1, 2, 3, 4} → 4 numbers

Conditions: 2 >= 3 ✗ FAILS!

Therefore, the minimum x is 5. One valid assignment would be:

  • arr1 = [1, 3, 5] (3 odd numbers)
  • arr2 = [2, 4] (2 numbers not divisible by 3)

The maximum value used is 5, which is our answer.

Solution Implementation

1from math import lcm
2from bisect import bisect_left
3
4class Solution:
5    def minimizeSet(
6        self, divisor1: int, divisor2: int, uniqueCnt1: int, uniqueCnt2: int
7    ) -> int:
8        """
9        Find the minimum possible maximum integer in two sets where:
10        - Set 1 needs uniqueCnt1 elements not divisible by divisor1
11        - Set 2 needs uniqueCnt2 elements not divisible by divisor2
12        - Elements must be positive integers and can't be in both sets
13        """
14      
15        def can_form_sets(max_value):
16            """
17            Check if we can form valid sets with maximum value <= max_value
18          
19            For each set, count how many numbers from 1 to max_value 
20            are not divisible by the respective divisor
21            """
22            # Count numbers from 1 to max_value not divisible by divisor1
23            # Formula: total - (numbers divisible by divisor1)
24            available_for_set1 = max_value - (max_value // divisor1)
25          
26            # Count numbers from 1 to max_value not divisible by divisor2
27            available_for_set2 = max_value - (max_value // divisor2)
28          
29            # Count numbers from 1 to max_value not divisible by LCM of both divisors
30            # These numbers can be placed in either set
31            lcm_divisor = lcm(divisor1, divisor2)
32            available_for_both = max_value - (max_value // lcm_divisor)
33          
34            # Check if all constraints can be satisfied
35            return (
36                available_for_set1 >= uniqueCnt1 and  # Set 1 has enough valid numbers
37                available_for_set2 >= uniqueCnt2 and  # Set 2 has enough valid numbers
38                available_for_both >= uniqueCnt1 + uniqueCnt2  # Total unique numbers sufficient
39            )
40      
41        # Binary search for the minimum value that satisfies all constraints
42        # Search range: [0, 10^10), which is large enough for most cases
43        return bisect_left(range(10**10), True, key=can_form_sets)
44
1class Solution {
2    /**
3     * Finds the minimum possible maximum value when creating two arrays with given constraints.
4     * Array1 cannot contain multiples of divisor1 and must have uniqueCnt1 distinct elements.
5     * Array2 cannot contain multiples of divisor2 and must have uniqueCnt2 distinct elements.
6     * 
7     * @param divisor1 The divisor constraint for the first array
8     * @param divisor2 The divisor constraint for the second array
9     * @param uniqueCnt1 Required number of distinct elements in the first array
10     * @param uniqueCnt2 Required number of distinct elements in the second array
11     * @return The minimum possible maximum value among all elements in both arrays
12     */
13    public int minimizeSet(int divisor1, int divisor2, int uniqueCnt1, int uniqueCnt2) {
14        // Calculate LCM to find numbers divisible by both divisors
15        long leastCommonMultiple = lcm(divisor1, divisor2);
16      
17        // Binary search bounds for finding the minimum maximum value
18        long left = 1;
19        long right = 10_000_000_000L;
20      
21        // Binary search to find the minimum valid maximum value
22        while (left < right) {
23            long mid = (left + right) >> 1;  // Equivalent to (left + right) / 2
24          
25            // Count available numbers for array1 (not divisible by divisor1)
26            // Numbers from 1 to mid that are NOT divisible by divisor1
27            long availableForArray1 = mid / divisor1 * (divisor1 - 1) + mid % divisor1;
28          
29            // Count available numbers for array2 (not divisible by divisor2)
30            // Numbers from 1 to mid that are NOT divisible by divisor2
31            long availableForArray2 = mid / divisor2 * (divisor2 - 1) + mid % divisor2;
32          
33            // Count total available numbers (not divisible by LCM)
34            // This represents numbers that can be used in either array
35            long totalAvailable = mid / leastCommonMultiple * (leastCommonMultiple - 1) + 
36                                 mid % leastCommonMultiple;
37          
38            // Check if current mid value satisfies all constraints
39            if (availableForArray1 >= uniqueCnt1 && 
40                availableForArray2 >= uniqueCnt2 && 
41                totalAvailable >= uniqueCnt1 + uniqueCnt2) {
42                // Mid is valid, try to find a smaller value
43                right = mid;
44            } else {
45                // Mid is too small, need a larger value
46                left = mid + 1;
47            }
48        }
49      
50        return (int) left;
51    }
52  
53    /**
54     * Calculates the least common multiple of two integers.
55     * 
56     * @param a First integer
57     * @param b Second integer
58     * @return The LCM of a and b
59     */
60    private long lcm(int a, int b) {
61        return (long) a * b / gcd(a, b);
62    }
63  
64    /**
65     * Calculates the greatest common divisor using Euclidean algorithm.
66     * 
67     * @param a First integer
68     * @param b Second integer
69     * @return The GCD of a and b
70     */
71    private int gcd(int a, int b) {
72        return b == 0 ? a : gcd(b, a % b);
73    }
74}
75
1class Solution {
2public:
3    int minimizeSet(int divisor1, int divisor2, int uniqueCnt1, int uniqueCnt2) {
4        // Binary search range: from 1 to a large upper bound
5        long left = 1, right = 1e10;
6      
7        // Calculate LCM of both divisors to handle numbers divisible by both
8        long lcmValue = lcm(static_cast<long>(divisor1), static_cast<long>(divisor2));
9      
10        // Binary search for the minimum possible maximum value
11        while (left < right) {
12            long mid = (left + right) >> 1;  // Equivalent to (left + right) / 2
13          
14            // Count numbers from 1 to mid that are NOT divisible by divisor1
15            // Formula: (full cycles * numbers per cycle) + remainder
16            long availableForSet1 = mid / divisor1 * (divisor1 - 1) + mid % divisor1;
17          
18            // Count numbers from 1 to mid that are NOT divisible by divisor2
19            long availableForSet2 = mid / divisor2 * (divisor2 - 1) + mid % divisor2;
20          
21            // Count numbers from 1 to mid that are NOT divisible by either divisor
22            // These numbers can be used in either set
23            long availableForBoth = mid / lcmValue * (lcmValue - 1) + mid % lcmValue;
24          
25            // Check if current mid value satisfies all constraints:
26            // 1. Enough numbers not divisible by divisor1 for set1
27            // 2. Enough numbers not divisible by divisor2 for set2
28            // 3. Enough total unique numbers for both sets combined
29            if (availableForSet1 >= uniqueCnt1 && 
30                availableForSet2 >= uniqueCnt2 && 
31                availableForBoth >= uniqueCnt1 + uniqueCnt2) {
32                // Mid is valid, try to find smaller value
33                right = mid;
34            } else {
35                // Mid is too small, need larger value
36                left = mid + 1;
37            }
38        }
39      
40        // Return the minimum valid maximum value
41        return left;
42    }
43};
44
1function minimizeSet(divisor1: number, divisor2: number, uniqueCnt1: number, uniqueCnt2: number): number {
2    // Binary search range: from 1 to a large upper bound
3    let left = 1;
4    let right = 10000000000;
5  
6    // Calculate LCM of both divisors to handle numbers divisible by both
7    const lcmValue = lcm(divisor1, divisor2);
8  
9    // Binary search for the minimum possible maximum value
10    while (left < right) {
11        // Calculate midpoint using bit shift for efficiency
12        const mid = Math.floor((left + right) / 2);
13      
14        // Count numbers from 1 to mid that are NOT divisible by divisor1
15        // These can be placed in set1
16        const availableForSet1 = Math.floor(mid / divisor1) * (divisor1 - 1) + (mid % divisor1);
17      
18        // Count numbers from 1 to mid that are NOT divisible by divisor2
19        // These can be placed in set2
20        const availableForSet2 = Math.floor(mid / divisor2) * (divisor2 - 1) + (mid % divisor2);
21      
22        // Count numbers from 1 to mid that are NOT divisible by either divisor
23        // These flexible numbers can be used in either set
24        const availableForBoth = Math.floor(mid / lcmValue) * (lcmValue - 1) + (mid % lcmValue);
25      
26        // Check if current mid value satisfies all constraints:
27        // 1. Enough numbers not divisible by divisor1 for set1
28        // 2. Enough numbers not divisible by divisor2 for set2
29        // 3. Enough total unique numbers for both sets combined
30        if (availableForSet1 >= uniqueCnt1 && 
31            availableForSet2 >= uniqueCnt2 && 
32            availableForBoth >= uniqueCnt1 + uniqueCnt2) {
33            // Mid is valid, try to find a smaller maximum value
34            right = mid;
35        } else {
36            // Mid is too small, need a larger maximum value
37            left = mid + 1;
38        }
39    }
40  
41    // Return the minimum valid maximum value
42    return left;
43}
44
45// Helper function to calculate the greatest common divisor
46function gcd(a: number, b: number): number {
47    while (b !== 0) {
48        const temp = b;
49        b = a % b;
50        a = temp;
51    }
52    return a;
53}
54
55// Helper function to calculate the least common multiple
56function lcm(a: number, b: number): number {
57    return Math.floor((a * b) / gcd(a, b));
58}
59

Time and Space Complexity

Time Complexity: O(log(10^10) × log(divisor1 × divisor2))

The binary search using bisect_left searches in the range [0, 10^10), which takes O(log(10^10)) iterations. For each iteration, the function f(x) is called, which performs:

  • Division and modulo operations with divisor1, divisor2, and divisor (LCM of divisor1 and divisor2): O(1)
  • The LCM calculation lcm(divisor1, divisor2) involves computing the GCD using Euclidean algorithm: O(log(min(divisor1, divisor2)))

Since the LCM is calculated once outside the binary search, and assuming the LCM function internally uses GCD with time complexity O(log(divisor1 × divisor2)), the overall time complexity is O(log(divisor1 × divisor2) + log(10^10)), which simplifies to O(log(10^10)) as the dominant term.

Space Complexity: O(1)

The algorithm uses only a constant amount of extra space for:

  • Variables cnt1, cnt2, cnt in function f
  • Variable divisor to store the LCM
  • The binary search mechanism in bisect_left uses O(1) space as it works with indices

The range(10^10) object is a generator that doesn't create the entire list in memory, so it contributes O(1) space.

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

Common Pitfalls

Pitfall 1: Incorrect Counting of Available Numbers

The Problem: A common mistake is incorrectly calculating how many numbers from 1 to x are not divisible by a given divisor. The naive approach might try to iterate and count, or use an incorrect formula like x - x // divisor which doesn't account for the pattern correctly.

Why It Happens: The formula x - (x // divisor) seems intuitive - total numbers minus divisible numbers. However, this is exactly what we want! The confusion often comes from overthinking or trying to use modular arithmetic incorrectly, such as x // divisor * (divisor - 1) + x % divisor which would count incorrectly.

The Solution: Use the straightforward formula: available = x - (x // divisor)

  • This directly counts non-divisible numbers
  • For example, with x = 10 and divisor = 3: we have 10 - (10//3) = 10 - 3 = 7 numbers not divisible by 3 (which are: 1, 2, 4, 5, 7, 8, 10)

Pitfall 2: Forgetting the Disjoint Constraint

The Problem: Some solutions only check if there are enough valid numbers for each array independently (available_for_set1 >= uniqueCnt1 and available_for_set2 >= uniqueCnt2) but forget to verify that we have enough total unique numbers to fill both arrays without overlap.

Why It Happens: It's easy to focus on each array's individual requirements and miss the global constraint that no number can appear in both arrays.

The Solution: Always include the third condition: available_for_both >= uniqueCnt1 + uniqueCnt2

  • This ensures the total pool of available numbers (those not divisible by LCM) is large enough
  • Numbers divisible by only one divisor are forced into one specific array
  • Numbers divisible by neither can go in either array, providing flexibility

Pitfall 3: Integer Overflow with LCM Calculation

The Problem: When calculating lcm(divisor1, divisor2), the result can be very large, potentially causing integer overflow in languages with fixed integer sizes or leading to performance issues.

Why It Happens: LCM can grow quickly. For example, if divisor1 = 10^5 and divisor2 = 10^5 - 1, the LCM could be close to 10^10.

The Solution:

  • In Python, this is less of an issue due to arbitrary precision integers
  • For other languages, use careful overflow checking or long/BigInteger types
  • Consider using the formula: lcm(a, b) = a * b // gcd(a, b) with GCD calculation to avoid intermediate overflow
  • Set appropriate upper bounds for binary search based on problem constraints

Pitfall 4: Inefficient Binary Search Bounds

The Problem: Using an unnecessarily large search range like range(10**10) when a smaller bound would suffice, leading to more iterations than needed.

Why It Happens: Without analyzing the problem constraints, it's tempting to use a "safe" large number that will definitely contain the answer.

The Solution: Calculate a tighter upper bound based on the problem:

# The answer is at most the sum of counts plus some buffer for divisibility constraints
upper_bound = (uniqueCnt1 + uniqueCnt2) * max(divisor1, divisor2)
return bisect_left(range(upper_bound + 1), True, key=can_form_sets)

This reduces the number of binary search iterations while still guaranteeing correctness.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using the following tree as input?

1def serialize(root):
2    res = []
3    def dfs(root):
4        if not root:
5            res.append('x')
6            return
7        res.append(root.val)
8        dfs(root.left)
9        dfs(root.right)
10    dfs(root)
11    return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4    StringJoiner res = new StringJoiner(" ");
5    serializeDFS(root, res);
6    return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10    if (root == null) {
11        result.add("x");
12        return;
13    }
14    result.add(Integer.toString(root.val));
15    serializeDFS(root.left, result);
16    serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2    let res = [];
3    serialize_dfs(root, res);
4    return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8    if (!root) {
9        res.push("x");
10        return;
11    }
12    res.push(root.val);
13    serialize_dfs(root.left, res);
14    serialize_dfs(root.right, res);
15}
16

Recommended Readings

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

Load More