1589. Maximum Sum Obtained of Any Permutation
Problem Description
You are given an array of integers nums
and an array of requests
. Each request requests[i] = [start_i, end_i]
asks for the sum of elements from index start_i
to end_i
(inclusive) in the nums
array. Both indices are 0-indexed.
Your task is to rearrange the elements in nums
in any order (find a permutation) such that when you calculate the sum for each request and add all these sums together, you get the maximum possible total sum.
For example, if nums = [1, 2, 3]
and requests = [[0, 1], [1, 2]]
:
- Request
[0, 1]
calculatesnums[0] + nums[1]
- Request
[1, 2]
calculatesnums[1] + nums[2]
- The total sum would be
(nums[0] + nums[1]) + (nums[1] + nums[2])
Notice that nums[1]
appears in both requests, so it contributes twice to the total sum. To maximize the result, you'd want to place the largest available number at index 1.
The key insight is that indices that appear in more requests should be assigned larger values from nums
to maximize the total sum. The solution uses a difference array to count how many times each index appears across all requests, then pairs the most frequently requested indices with the largest values in nums
.
Return the maximum total sum modulo 10^9 + 7
.
Intuition
The core observation is that each index in the array contributes to the total sum based on how many requests include it. If an index appears in multiple requests, its value gets added multiple times to the final result.
Think of it this way: if index i
appears in 5 different requests, then nums[i]
will be added 5 times to our total sum. If another index j
appears in only 1 request, then nums[j]
contributes only once.
This leads to a greedy strategy: we should assign the largest values from nums
to the indices that appear most frequently in requests. By pairing high-frequency indices with large values, we maximize the contribution to the total sum.
To count how many times each index appears across all requests, we can use a difference array technique. For each request [l, r]
, all indices from l
to r
appear once. Instead of incrementing each index individually (which would be inefficient), we mark +1
at the start position l
and -1
at position r+1
. After processing all requests, a prefix sum gives us the frequency count for each index.
Once we have the frequency counts and sort both the nums
array and frequency array in ascending order, we can pair them up: the smallest frequency with the smallest value, the second smallest frequency with the second smallest value, and so on. This ensures that the indices with highest frequencies get the largest values, maximizing our total sum.
The final answer is the sum of nums[i] * frequency[i]
for all indices, taken modulo 10^9 + 7
.
Learn more about Greedy, Prefix Sum and Sorting patterns.
Solution Approach
The implementation follows these key steps:
Step 1: Build the Difference Array
We create a difference array d
of size n
(same as nums
) initialized with zeros. For each request [l, r]
:
- Increment
d[l]
by 1 (marking the start of the range) - Decrement
d[r + 1]
by 1 ifr + 1 < n
(marking the end of the range)
d = [0] * n for l, r in requests: d[l] += 1 if r + 1 < n: d[r + 1] -= 1
Step 2: Convert Difference Array to Frequency Counts
Apply prefix sum to transform the difference array into actual frequency counts. After this step, d[i]
represents how many times index i
appears across all requests.
for i in range(1, n):
d[i] += d[i - 1]
Step 3: Sort Both Arrays
Sort both nums
and the frequency array d
in ascending order. This prepares them for optimal pairing.
nums.sort() d.sort()
Step 4: Calculate Maximum Sum
Pair up the sorted arrays element by element. The smallest frequency gets paired with the smallest value, ensuring the largest frequencies get the largest values. Multiply each pair and sum them up.
return sum(a * b for a, b in zip(nums, d)) % mod
The time complexity is O(n log n + m)
where n
is the length of nums
and m
is the number of requests. The sorting operations dominate the complexity. The space complexity is O(n)
for the difference array.
The modulo operation 10^9 + 7
is applied at the end to prevent integer overflow and meet the problem's requirements.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to understand the solution approach.
Given:
nums = [3, 5, 1, 2]
requests = [[0, 2], [1, 3], [1, 1]]
Step 1: Build the Difference Array
We start with a difference array d = [0, 0, 0, 0]
(same size as nums).
Processing each request:
-
Request
[0, 2]
: Mark start at index 0 and end after index 2d[0] += 1
→d = [1, 0, 0, 0]
d[3] -= 1
→d = [1, 0, 0, -1]
-
Request
[1, 3]
: Mark start at index 1 and end after index 3d[1] += 1
→d = [1, 1, 0, -1]
- Since
3 + 1 = 4
is out of bounds, we skip the decrement
-
Request
[1, 1]
: Mark start at index 1 and end after index 1d[1] += 1
→d = [1, 2, 0, -1]
d[2] -= 1
→d = [1, 2, -1, -1]
Step 2: Convert to Frequency Counts via Prefix Sum
Apply prefix sum to get actual frequencies:
d[0] = 1
(stays the same)d[1] = d[0] + d[1] = 1 + 2 = 3
d[2] = d[1] + d[2] = 3 + (-1) = 2
d[3] = d[2] + d[3] = 2 + (-1) = 1
Result: d = [1, 3, 2, 1]
This means:
- Index 0 appears in 1 request
- Index 1 appears in 3 requests
- Index 2 appears in 2 requests
- Index 3 appears in 1 request
Step 3: Sort Both Arrays
- Sort
nums
:[1, 2, 3, 5]
- Sort
d
:[1, 1, 2, 3]
Step 4: Calculate Maximum Sum
Pair up sorted arrays (smallest with smallest, largest with largest):
1 × 1 = 1
2 × 1 = 2
3 × 2 = 6
5 × 3 = 15
Total sum = 1 + 2 + 6 + 15 = 24
Verification:
The optimal arrangement would place values as: nums = [2, 5, 3, 1]
- Request
[0, 2]
: sum = 2 + 5 + 3 = 10 - Request
[1, 3]
: sum = 5 + 3 + 1 = 9 - Request
[1, 1]
: sum = 5
Total = 10 + 9 + 5 = 24 ✓
Solution Implementation
1class Solution:
2 def maxSumRangeQuery(self, nums: List[int], requests: List[List[int]]) -> int:
3 """
4 Calculate maximum sum after applying all range sum queries.
5 Strategy: Match highest frequency positions with largest numbers.
6
7 Args:
8 nums: List of integers to query
9 requests: List of [left, right] range queries
10
11 Returns:
12 Maximum possible sum modulo 10^9 + 7
13 """
14 n = len(nums)
15
16 # Create difference array to track frequency of each index
17 # Using difference array technique for efficient range updates
18 frequency = [0] * n
19
20 # Mark range boundaries in difference array
21 # Increment at start, decrement after end
22 for left, right in requests:
23 frequency[left] += 1
24 if right + 1 < n:
25 frequency[right + 1] -= 1
26
27 # Convert difference array to actual frequencies using prefix sum
28 for i in range(1, n):
29 frequency[i] += frequency[i - 1]
30
31 # Sort both arrays to match highest frequencies with largest numbers
32 nums.sort()
33 frequency.sort()
34
35 # Calculate maximum sum by pairing sorted values
36 # Multiply each number by its frequency and sum all products
37 MOD = 10**9 + 7
38 max_sum = sum(num * freq for num, freq in zip(nums, frequency)) % MOD
39
40 return max_sum
41
1class Solution {
2 public int maxSumRangeQuery(int[] nums, int[][] requests) {
3 int n = nums.length;
4
5 // Create a difference array to track frequency of each index
6 int[] frequency = new int[n];
7
8 // Apply difference array technique for each request
9 // Increment at start of range, decrement after end of range
10 for (int[] request : requests) {
11 int left = request[0];
12 int right = request[1];
13
14 frequency[left]++;
15 if (right + 1 < n) {
16 frequency[right + 1]--;
17 }
18 }
19
20 // Convert difference array to actual frequency counts using prefix sum
21 for (int i = 1; i < n; i++) {
22 frequency[i] += frequency[i - 1];
23 }
24
25 // Sort both arrays to pair largest numbers with highest frequencies
26 Arrays.sort(nums);
27 Arrays.sort(frequency);
28
29 // Calculate maximum sum by pairing sorted values
30 final int MOD = (int) 1e9 + 7;
31 long totalSum = 0;
32
33 for (int i = 0; i < n; i++) {
34 // Multiply each number by its corresponding frequency and add to sum
35 totalSum = (totalSum + (long) nums[i] * frequency[i]) % MOD;
36 }
37
38 return (int) totalSum;
39 }
40}
41
1class Solution {
2public:
3 int maxSumRangeQuery(vector<int>& nums, vector<vector<int>>& requests) {
4 int n = nums.size();
5
6 // Create difference array to track frequency of each index
7 vector<int> frequency(n, 0);
8
9 // Apply difference array technique to efficiently count overlapping ranges
10 // For each request [left, right], increment at left and decrement at right+1
11 for (const auto& request : requests) {
12 int left = request[0];
13 int right = request[1];
14 frequency[left]++;
15 if (right + 1 < n) {
16 frequency[right + 1]--;
17 }
18 }
19
20 // Convert difference array to actual frequency count using prefix sum
21 for (int i = 1; i < n; ++i) {
22 frequency[i] += frequency[i - 1];
23 }
24
25 // Sort nums array in ascending order
26 sort(nums.begin(), nums.end());
27
28 // Sort frequency array in ascending order
29 sort(frequency.begin(), frequency.end());
30
31 // Calculate maximum sum by pairing largest numbers with highest frequencies
32 long long maxSum = 0;
33 const int MOD = 1e9 + 7;
34
35 for (int i = 0; i < n; ++i) {
36 // Multiply each number with its corresponding frequency and add to sum
37 maxSum = (maxSum + 1LL * nums[i] * frequency[i]) % MOD;
38 }
39
40 return maxSum;
41 }
42};
43
1/**
2 * Calculates the maximum sum of all requests after optimally arranging numbers
3 * @param nums - Array of numbers to be arranged
4 * @param requests - Array of [left, right] index pairs representing query ranges
5 * @returns Maximum sum modulo 10^9 + 7
6 */
7function maxSumRangeQuery(nums: number[], requests: number[][]): number {
8 const n: number = nums.length;
9
10 // Create frequency array to track how many times each index is queried
11 const frequency: number[] = new Array(n).fill(0);
12
13 // Use difference array technique to efficiently count query frequencies
14 for (const [left, right] of requests) {
15 frequency[left]++;
16 if (right + 1 < n) {
17 frequency[right + 1]--;
18 }
19 }
20
21 // Convert difference array to actual frequency counts using prefix sum
22 for (let i = 1; i < n; i++) {
23 frequency[i] += frequency[i - 1];
24 }
25
26 // Sort numbers in ascending order
27 nums.sort((a: number, b: number) => a - b);
28
29 // Sort frequencies in ascending order
30 frequency.sort((a: number, b: number) => a - b);
31
32 // Calculate maximum sum by pairing largest numbers with highest frequencies
33 let maxSum: number = 0;
34 const MOD: number = 10 ** 9 + 7;
35
36 for (let i = 0; i < n; i++) {
37 maxSum = (maxSum + nums[i] * frequency[i]) % MOD;
38 }
39
40 return maxSum;
41}
42
Time and Space Complexity
Time Complexity: O(n × log n)
, where n
is the length of the array nums
.
The time complexity is dominated by the sorting operations:
- Building the difference array
d
with requests takesO(m)
time, wherem
is the number of requests - Computing the prefix sum of the difference array takes
O(n)
time - Sorting
nums
takesO(n × log n)
time - Sorting the frequency array
d
takesO(n × log n)
time - Computing the final sum with
zip
takesO(n)
time
Since O(n × log n)
is the largest term, the overall time complexity is O(n × log n)
.
Space Complexity: O(n)
, where n
is the length of the array nums
.
The space complexity comes from:
- The difference array
d
of sizen
, which requiresO(n)
space - The sorting operations on
nums
andd
may useO(n)
additional space depending on the implementation (Python's Timsort uses up toO(n)
auxiliary space) - The
zip
operation creates an iterator which usesO(1)
space
Therefore, the overall space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Apply Modulo Operation
Pitfall: The sum can become extremely large, potentially exceeding integer limits. Forgetting to apply the modulo operation can lead to incorrect results or overflow issues.
Wrong approach:
# Missing modulo operation
return sum(num * freq for num, freq in zip(nums, frequency))
Correct approach:
MOD = 10**9 + 7
return sum(num * freq for num, freq in zip(nums, frequency)) % MOD
2. Incorrect Difference Array Boundary Handling
Pitfall: When building the difference array, it's crucial to check if right + 1
is within bounds before decrementing. Blindly accessing frequency[right + 1]
can cause an IndexError.
Wrong approach:
for left, right in requests: frequency[left] += 1 frequency[right + 1] -= 1 # IndexError when right == n-1
Correct approach:
for left, right in requests: frequency[left] += 1 if right + 1 < n: # Boundary check frequency[right + 1] -= 1
3. Misunderstanding the Difference Array Technique
Pitfall: Some might try to increment every index in the range directly, which is inefficient and defeats the purpose of using a difference array.
Wrong approach (O(n*m) complexity):
frequency = [0] * n
for left, right in requests:
for i in range(left, right + 1):
frequency[i] += 1 # Inefficient
Correct approach (O(m) for updates):
frequency = [0] * n for left, right in requests: frequency[left] += 1 if right + 1 < n: frequency[right + 1] -= 1 # Then apply prefix sum once
4. Forgetting to Convert Difference Array to Actual Frequencies
Pitfall: After marking the boundaries in the difference array, you must apply prefix sum to get the actual frequency counts. Skipping this step will give incorrect frequencies.
Wrong approach:
# Build difference array for left, right in requests: frequency[left] += 1 if right + 1 < n: frequency[right + 1] -= 1 # Forgot to apply prefix sum! nums.sort() frequency.sort() # This sorts the difference array, not frequencies
Correct approach:
# Build difference array
for left, right in requests:
frequency[left] += 1
if right + 1 < n:
frequency[right + 1] -= 1
# Convert to actual frequencies
for i in range(1, n):
frequency[i] += frequency[i - 1]
nums.sort()
frequency.sort()
5. Modifying Original Input Array
Pitfall: Sorting nums
in-place modifies the original array, which might not be desired in some contexts or could affect test cases if the array is reused.
Solution if preserving original is needed:
nums_copy = nums.copy() # Create a copy nums_copy.sort() # Use nums_copy for calculation
Is the following code DFS or BFS?
void search(Node root) { if (!root) return; visit(root); root.visited = true; for (Node node in root.adjacent) { if (!node.visited) { search(node); } } }
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Want a Structured Path to Master System Design Too? Don’t Miss This!