Facebook Pixel

2834. Find the Minimum Possible Sum of a Beautiful Array

Problem Description

You need to create an array of positive integers with specific properties and find its minimum possible sum.

Given two positive integers n and target, you must construct a "beautiful" array that satisfies these three conditions:

  1. The array has exactly n elements
  2. All elements are distinct positive integers (no duplicates allowed)
  3. No two different elements in the array can sum to target (if nums[i] + nums[j] == target for any i ≠ j, the array is not beautiful)

Your task is to find the minimum possible sum of all elements in such a beautiful array, and return this sum modulo 10^9 + 7.

For example, if n = 3 and target = 6, you cannot use both 2 and 4 in your array since 2 + 4 = 6. To minimize the sum, you'd want to use the smallest possible positive integers while avoiding pairs that sum to the target. One valid beautiful array could be [1, 2, 7] (where we skip 3, 4, 5 to avoid creating pairs that sum to 6), giving a sum of 10.

The key insight is that if you include a number x in your array, you cannot include target - x to maintain the beautiful property. This creates a constraint on which numbers you can select when trying to minimize the total sum.

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

Intuition

To minimize the sum, we want to use the smallest possible positive integers. Let's start by trying to pick 1, 2, 3, ... in order.

The key constraint is that if we pick a number x, we cannot pick target - x. This creates pairs that are "forbidden" together. For instance, if target = 10, then (1, 9), (2, 8), (3, 7), (4, 6) are forbidden pairs - we can only pick one number from each pair.

Notice that when x gets close to target/2, we're essentially choosing between two numbers that are roughly equal. For example, with target = 10, the pair (4, 6) has numbers close in value, while (1, 9) has numbers far apart. To minimize our sum, we should always pick the smaller number from each forbidden pair.

This leads us to a critical observation: we can safely pick all numbers from 1 up to ⌊target/2⌋ because for each of these numbers x, its forbidden partner target - x is larger than target/2, so we're always choosing the smaller option.

Let's call m = ⌊target/2⌋. If we need n numbers and n ≤ m, we can simply take 1, 2, 3, ..., n without any conflicts. The sum would be (1 + n) * n / 2.

But what if n > m? We can take all numbers from 1 to m (that's m numbers), but we still need n - m more numbers. Since we've exhausted all numbers less than target/2, we now need to pick numbers that don't have forbidden partners in our chosen set. The safe choice is to start from target itself (since no two numbers in our array can sum to create target, the number target has no forbidden partner in our array) and continue with target + 1, target + 2, and so on.

So when n > m, our array consists of:

  • The numbers 1, 2, ..., m with sum (1 + m) * m / 2
  • The numbers target, target + 1, ..., target + (n - m - 1) with sum (target + target + n - m - 1) * (n - m) / 2

This greedy approach guarantees we're always picking the smallest available numbers while respecting the constraint, thus minimizing the total sum.

Learn more about Greedy and Math patterns.

Solution Approach

The implementation follows a greedy mathematical approach based on our intuition about forbidden pairs.

First, we define the modulo value mod = 10^9 + 7 as required by the problem.

We calculate m = target // 2, which represents the threshold - all numbers from 1 to m can be safely included without creating any pairs that sum to target.

Case 1: When n <= m

If we need at most m numbers, we can simply take the first n positive integers: 1, 2, 3, ..., n. These are the smallest possible positive integers, and none of their pairs sum to target (since the maximum possible sum would be (m-1) + m < target).

The sum is calculated using the arithmetic series formula:

return ((1 + n) * n // 2) % mod

Case 2: When n > m

We need more than m numbers, so we:

  1. Take all numbers from 1 to m (giving us m numbers)
  2. Skip numbers from m + 1 to target - 1 (these would create forbidden pairs with our already chosen numbers)
  3. Take numbers starting from target onwards: target, target + 1, ..., target + (n - m - 1)

The total sum consists of two parts:

  • Sum of 1 to m: (1 + m) * m // 2
  • Sum of n - m consecutive numbers starting from target: (target + target + n - m - 1) * (n - m) // 2

The second part uses the arithmetic series formula where:

  • First term = target
  • Last term = target + (n - m - 1)
  • Number of terms = n - m
return ((1 + m) * m // 2 + (target + target + n - m - 1) * (n - m) // 2) % mod

The algorithm runs in O(1) time complexity since we're only performing mathematical calculations without any loops or recursion. The space complexity is also O(1) as we only use a constant amount of extra space.

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 the solution with n = 5 and target = 8.

First, we calculate m = target // 2 = 8 // 2 = 4. This means we can safely use numbers 1 through 4 without creating any pairs that sum to 8.

Since n = 5 > m = 4, we fall into Case 2.

Step 1: Choose numbers from 1 to m We take: [1, 2, 3, 4]

  • Note: We can't also include 7, 6, 5, or 4 (already chosen) as they would form forbidden pairs:
    • 1 + 7 = 8 (forbidden)
    • 2 + 6 = 8 (forbidden)
    • 3 + 5 = 8 (forbidden)
    • 4 + 4 = 8 (but we only have one 4)

Step 2: We still need n - m = 5 - 4 = 1 more number Starting from target = 8, we take the next available number: [8]

  • Why 8 is safe: No other number in our array can sum with 8 to give 8 (that would require 0, which isn't a positive integer)

Final array: [1, 2, 3, 4, 8]

Calculating the sum:

  • Sum of 1 to 4: (1 + 4) * 4 / 2 = 5 * 2 = 10
  • Sum of numbers starting from 8 (just one number): 8
  • Total sum: 10 + 8 = 18

Using the formula:

  • Part 1: (1 + m) * m // 2 = (1 + 4) * 4 // 2 = 10
  • Part 2: (target + target + n - m - 1) * (n - m) // 2 = (8 + 8 + 0) * 1 // 2 = 8
  • Total: 10 + 8 = 18

The minimum possible sum is 18.

Solution Implementation

1class Solution:
2    def minimumPossibleSum(self, n: int, target: int) -> int:
3        # Modulo value for preventing integer overflow
4        MOD = 10**9 + 7
5      
6        # Calculate the maximum number we can use before reaching target/2
7        # We can't use both x and (target - x), so we can safely use numbers up to target//2
8        max_before_target_half = target // 2
9      
10        # Case 1: If n is small enough, we can just use numbers 1, 2, ..., n
11        if n <= max_before_target_half:
12            # Sum of first n natural numbers: n * (n + 1) / 2
13            sum_result = (1 + n) * n // 2
14            return sum_result % MOD
15      
16        # Case 2: We need more numbers than what's available before target/2
17        # First part: sum of numbers from 1 to max_before_target_half
18        sum_first_part = (1 + max_before_target_half) * max_before_target_half // 2
19      
20        # Second part: sum of numbers starting from target
21        # We need (n - max_before_target_half) numbers starting from target
22        remaining_count = n - max_before_target_half
23        # Sum of arithmetic sequence: first_term = target, last_term = target + remaining_count - 1
24        sum_second_part = (target + target + remaining_count - 1) * remaining_count // 2
25      
26        total_sum = sum_first_part + sum_second_part
27        return total_sum % MOD
28
1class Solution {
2    public int minimumPossibleSum(int n, int target) {
3        // Define the modulo constant for large number handling
4        final int MOD = (int) 1e9 + 7;
5      
6        // Calculate the midpoint - numbers from 1 to m can be used without forming target
7        // Any two numbers that sum to target cannot both be in our set
8        int midpoint = target / 2;
9      
10        // Case 1: If n is less than or equal to midpoint
11        // We can simply use numbers 1, 2, 3, ..., n
12        if (n <= midpoint) {
13            // Sum of first n natural numbers: n * (n + 1) / 2
14            return (int) ((1L + n) * n / 2 % MOD);
15        }
16      
17        // Case 2: If n is greater than midpoint
18        // We use numbers from 1 to midpoint, then skip to target and continue
19      
20        // Part 1: Sum of numbers from 1 to midpoint
21        // Using formula: midpoint * (midpoint + 1) / 2
22        long sumFirstPart = (1L + midpoint) * midpoint / 2 % MOD;
23      
24        // Part 2: Sum of numbers from target to (target + remaining count - 1)
25        // Remaining count = n - midpoint
26        // Sum = (first term + last term) * count / 2
27        // First term = target, Last term = target + (n - midpoint - 1)
28        long remainingCount = n - midpoint;
29        long sumSecondPart = ((1L * target + target + remainingCount - 1) * remainingCount / 2) % MOD;
30      
31        // Return the total sum modulo MOD
32        return (int) ((sumFirstPart + sumSecondPart) % MOD);
33    }
34}
35
1class Solution {
2public:
3    int minimumPossibleSum(int n, int target) {
4        // Define modulo constant for preventing integer overflow
5        const int MOD = 1000000007;
6      
7        // Calculate the maximum number we can use before reaching target/2
8        // If we use both x and (target - x), they sum to target (forbidden)
9        // So we can only use numbers from 1 to target/2 without creating forbidden pairs
10        int maxUsableBeforeTarget = target / 2;
11      
12        // Case 1: If n is small enough, we can just use first n natural numbers
13        if (n <= maxUsableBeforeTarget) {
14            // Sum of first n natural numbers: n * (n + 1) / 2
15            // Using 1LL to prevent overflow during multiplication
16            long long sum = (1LL + n) * n / 2 % MOD;
17            return sum;
18        }
19      
20        // Case 2: We need more than maxUsableBeforeTarget numbers
21        // We use numbers from 1 to maxUsableBeforeTarget, then skip to target and beyond
22      
23        // Calculate sum of numbers from 1 to maxUsableBeforeTarget
24        long long sumFirstPart = (1LL + maxUsableBeforeTarget) * maxUsableBeforeTarget / 2 % MOD;
25      
26        // Calculate how many more numbers we need
27        int remainingCount = n - maxUsableBeforeTarget;
28      
29        // Calculate sum of numbers from target to (target + remainingCount - 1)
30        // This is an arithmetic sequence: target, target+1, target+2, ..., target+remainingCount-1
31        // Sum = (first + last) * count / 2
32        long long sumSecondPart = (1LL * target + target + remainingCount - 1) * remainingCount / 2 % MOD;
33      
34        // Return the total sum modulo MOD
35        return (sumFirstPart + sumSecondPart) % MOD;
36    }
37};
38
1/**
2 * Calculates the minimum possible sum of n distinct positive integers
3 * where no two integers sum up to the target value.
4 * 
5 * The strategy is to use numbers from 1 to target/2, then skip to target and beyond,
6 * since any number x and (target - x) would sum to target and violate the constraint.
7 * 
8 * @param n - The count of distinct positive integers needed
9 * @param target - The target sum that no two selected integers should equal
10 * @returns The minimum possible sum modulo 10^9 + 7
11 */
12function minimumPossibleSum(n: number, target: number): number {
13    // Modulo value to prevent integer overflow
14    const MODULO: number = 10 ** 9 + 7;
15  
16    // Calculate the maximum number we can use before reaching target/2
17    // Numbers from 1 to maxBeforeTarget can be safely used
18    const maxBeforeTarget: number = target >> 1; // Equivalent to Math.floor(target / 2)
19  
20    // Case 1: If n is small enough, we can use consecutive integers starting from 1
21    if (n <= maxBeforeTarget) {
22        // Sum of first n natural numbers: n * (n + 1) / 2
23        const sumOfFirstN: number = ((1 + n) * n) / 2;
24        return sumOfFirstN % MODULO;
25    }
26  
27    // Case 2: We need more numbers than available before target/2
28    // Use all numbers from 1 to maxBeforeTarget, then continue from target onwards
29  
30    // Sum of numbers from 1 to maxBeforeTarget
31    const sumBeforeTarget: number = ((1 + maxBeforeTarget) * maxBeforeTarget) / 2;
32  
33    // Count of remaining numbers needed
34    const remainingCount: number = n - maxBeforeTarget;
35  
36    // Sum of arithmetic sequence starting from target for remainingCount terms
37    // Formula: (first + last) * count / 2
38    // where last = target + remainingCount - 1
39    const sumAfterTarget: number = ((target + target + remainingCount - 1) * remainingCount) / 2;
40  
41    // Return the total sum modulo MODULO
42    return (sumBeforeTarget + sumAfterTarget) % MODULO;
43}
44

Time and Space Complexity

The time complexity is O(1) because the algorithm performs a fixed number of arithmetic operations regardless of the input size. The code consists of:

  • A few variable assignments (mod, m)
  • A single conditional check (if n <= m)
  • A constant number of arithmetic operations (addition, multiplication, division, modulo)

All these operations execute in constant time, and there are no loops or recursive calls that depend on the input size.

The space complexity is O(1) because the algorithm uses only a fixed amount of extra space. The variables stored are:

  • mod: a constant integer
  • m: computed from target
  • The intermediate results of arithmetic expressions

The space usage does not grow with the input size n or target, maintaining constant space complexity.

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

Common Pitfalls

1. Integer Overflow in Arithmetic Operations

The Pitfall: The most critical issue in this solution is potential integer overflow when calculating sums, especially for large values of n and target. In Python, while integers can grow arbitrarily large, performing modulo operations after large multiplications can still cause performance issues. More importantly, if this code were translated to languages like Java or C++, integer overflow would cause incorrect results.

For example, when calculating (target + target + remaining_count - 1) * remaining_count // 2, the intermediate multiplication could exceed the maximum integer value before the modulo operation is applied.

The Solution: Apply modulo operations more frequently during calculations to keep numbers manageable:

def minimumPossibleSum(self, n: int, target: int) -> int:
    MOD = 10**9 + 7
    max_before_target_half = target // 2
  
    if n <= max_before_target_half:
        # Apply modulo after calculation to prevent overflow
        sum_result = (n * (n + 1) // 2) % MOD
        return sum_result
  
    # Calculate first part with modulo
    sum_first_part = (max_before_target_half * (max_before_target_half + 1) // 2) % MOD
  
    # Calculate second part with modulo
    remaining_count = n - max_before_target_half
    first_term = target % MOD
    last_term = (target + remaining_count - 1) % MOD
    sum_second_part = ((first_term + last_term) % MOD * remaining_count % MOD * pow(2, MOD - 2, MOD)) % MOD
  
    return (sum_first_part + sum_second_part) % MOD

2. Off-by-One Error in Boundary Calculation

The Pitfall: A common mistake is incorrectly determining which numbers can be safely included. The code uses target // 2 as the boundary, but developers might accidentally use target / 2 (float division) or (target - 1) // 2, leading to incorrect results.

The Solution: Be explicit about the boundary logic and add comments:

# For any number x where x <= target//2, its pair (target - x) will be >= target//2 + 1
# This ensures no two numbers in range [1, target//2] sum to target
max_safe_value = target // 2  # Use integer division explicitly

3. Incorrect Arithmetic Series Formula Application

The Pitfall: When calculating the sum of consecutive integers starting from target, it's easy to miscalculate the last term or the count of terms. The formula requires:

  • First term: target
  • Last term: target + (n - max_before_target_half - 1)
  • Number of terms: n - max_before_target_half

A common error is using target + n - max_before_target_half as the last term (forgetting to subtract 1).

The Solution: Break down the calculation with clear variable names:

remaining_count = n - max_before_target_half
first_value = target
last_value = target + remaining_count - 1  # -1 because we're counting from 0
sum_second_part = (first_value + last_value) * remaining_count // 2
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

In a binary min heap, the minimum element can be found in:


Recommended Readings

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

Load More