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 Implementation

1from math import lcm
2
3class Solution:
4    def minimizeSet(
5        self, divisor1: int, divisor2: int, uniqueCnt1: int, uniqueCnt2: int
6    ) -> int:
7        """
8        Find the minimum possible maximum integer in two sets where:
9        - Set 1 needs uniqueCnt1 elements not divisible by divisor1
10        - Set 2 needs uniqueCnt2 elements not divisible by divisor2
11        - Elements must be positive integers and can't be in both sets
12        """
13        lcm_divisor = lcm(divisor1, divisor2)
14
15        def feasible(max_value: int) -> bool:
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            available_for_set1 = max_value - (max_value // divisor1)
24
25            # Count numbers from 1 to max_value not divisible by divisor2
26            available_for_set2 = max_value - (max_value // divisor2)
27
28            # Count numbers from 1 to max_value not divisible by LCM of both divisors
29            available_for_both = max_value - (max_value // lcm_divisor)
30
31            # Check if all constraints can be satisfied
32            return (
33                available_for_set1 >= uniqueCnt1 and
34                available_for_set2 >= uniqueCnt2 and
35                available_for_both >= uniqueCnt1 + uniqueCnt2
36            )
37
38        # Binary search for the minimum value that satisfies all constraints
39        # Using the first_true_index template pattern
40        left, right = 1, 10**10
41        first_true_index = -1
42
43        while left <= right:
44            mid = (left + right) // 2
45            if feasible(mid):
46                first_true_index = mid
47                right = mid - 1
48            else:
49                left = mid + 1
50
51        return first_true_index
52
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 using the first_true_index template pattern
18        long left = 1;
19        long right = 10_000_000_000L;
20        long firstTrueIndex = -1;
21
22        while (left <= right) {
23            long mid = left + (right - left) / 2;
24
25            // Count available numbers for array1 (not divisible by divisor1)
26            long availableForArray1 = mid - (mid / divisor1);
27
28            // Count available numbers for array2 (not divisible by divisor2)
29            long availableForArray2 = mid - (mid / divisor2);
30
31            // Count total available numbers (not divisible by LCM)
32            long totalAvailable = mid - (mid / leastCommonMultiple);
33
34            // Check if current mid value satisfies all constraints (feasible condition)
35            if (availableForArray1 >= uniqueCnt1 &&
36                availableForArray2 >= uniqueCnt2 &&
37                totalAvailable >= uniqueCnt1 + uniqueCnt2) {
38                // Mid is feasible, record it and search for smaller value
39                firstTrueIndex = mid;
40                right = mid - 1;
41            } else {
42                // Mid is not feasible, search for larger value
43                left = mid + 1;
44            }
45        }
46
47        return (int) firstTrueIndex;
48    }
49
50    /**
51     * Calculates the least common multiple of two integers.
52     */
53    private long lcm(int a, int b) {
54        return (long) a * b / gcd(a, b);
55    }
56
57    /**
58     * Calculates the greatest common divisor using Euclidean algorithm.
59     */
60    private int gcd(int a, int b) {
61        return b == 0 ? a : gcd(b, a % b);
62    }
63}
64
1class Solution {
2public:
3    int minimizeSet(int divisor1, int divisor2, int uniqueCnt1, int uniqueCnt2) {
4        // Calculate LCM of both divisors to handle numbers divisible by both
5        long long lcmValue = lcm(static_cast<long long>(divisor1), static_cast<long long>(divisor2));
6
7        // Binary search using the first_true_index template pattern
8        long long left = 1;
9        long long right = 10000000000LL;
10        long long firstTrueIndex = -1;
11
12        while (left <= right) {
13            long long mid = left + (right - left) / 2;
14
15            // Count numbers from 1 to mid that are NOT divisible by divisor1
16            long long availableForSet1 = mid - (mid / divisor1);
17
18            // Count numbers from 1 to mid that are NOT divisible by divisor2
19            long long availableForSet2 = mid - (mid / divisor2);
20
21            // Count numbers from 1 to mid that are NOT divisible by either divisor
22            long long availableForBoth = mid - (mid / lcmValue);
23
24            // Check if current mid value satisfies all constraints (feasible condition)
25            if (availableForSet1 >= uniqueCnt1 &&
26                availableForSet2 >= uniqueCnt2 &&
27                availableForBoth >= uniqueCnt1 + uniqueCnt2) {
28                // Mid is feasible, record it and search for smaller value
29                firstTrueIndex = mid;
30                right = mid - 1;
31            } else {
32                // Mid is not feasible, search for larger value
33                left = mid + 1;
34            }
35        }
36
37        return firstTrueIndex;
38    }
39};
40
1function minimizeSet(divisor1: number, divisor2: number, uniqueCnt1: number, uniqueCnt2: number): number {
2    // Calculate LCM of both divisors to handle numbers divisible by both
3    const lcmValue = lcm(divisor1, divisor2);
4
5    // Binary search using the first_true_index template pattern
6    let left = 1;
7    let right = 10000000000;
8    let firstTrueIndex = -1;
9
10    while (left <= right) {
11        const mid = Math.floor((left + right) / 2);
12
13        // Count numbers from 1 to mid that are NOT divisible by divisor1
14        const availableForSet1 = mid - Math.floor(mid / divisor1);
15
16        // Count numbers from 1 to mid that are NOT divisible by divisor2
17        const availableForSet2 = mid - Math.floor(mid / divisor2);
18
19        // Count numbers from 1 to mid that are NOT divisible by either divisor
20        const availableForBoth = mid - Math.floor(mid / lcmValue);
21
22        // Check if current mid value satisfies all constraints (feasible condition)
23        if (availableForSet1 >= uniqueCnt1 &&
24            availableForSet2 >= uniqueCnt2 &&
25            availableForBoth >= uniqueCnt1 + uniqueCnt2) {
26            // Mid is feasible, record it and search for smaller value
27            firstTrueIndex = mid;
28            right = mid - 1;
29        } else {
30            // Mid is not feasible, search for larger value
31            left = mid + 1;
32        }
33    }
34
35    return firstTrueIndex;
36}
37
38// Helper function to calculate the greatest common divisor
39function gcd(a: number, b: number): number {
40    while (b !== 0) {
41        const temp = b;
42        b = a % b;
43        a = temp;
44    }
45    return a;
46}
47
48// Helper function to calculate the least common multiple
49function lcm(a: number, b: number): number {
50    return Math.floor((a * b) / gcd(a, b));
51}
52

Solution Approach

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

Binary Search Template Setup: We use the standard template with left, right, and first_true_index:

left, right = 1, 10**10
first_true_index = -1

while left <= right:
    mid = (left + right) // 2
    if feasible(mid):
        first_true_index = mid
        right = mid - 1
    else:
        left = mid + 1

return first_true_index

The Feasible Function: 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.

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: Using Wrong Binary Search Template Variant

The Problem: A common mistake is using the while left < right with right = mid pattern instead of the standard template with first_true_index.

Why It Happens: Many developers learn different binary search variants and may default to a pattern that doesn't match the canonical template.

The Solution: Always use the first_true_index template pattern:

left, right = 1, 10**10
first_true_index = -1

while left <= right:
    mid = (left + right) // 2
    if feasible(mid):
        first_true_index = mid
        right = mid - 1
    else:
        left = mid + 1

return first_true_index

This template ensures consistency across all binary search problems and clearly tracks the answer in first_true_index.

Pitfall 2: 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 3: 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 4: 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 5: 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.

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

Which algorithm is best for finding the shortest distance between two points in an unweighted graph?


Recommended Readings

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

Load More