Sum of Subarray Minimums
Problem
Given an array of integers arr, find the sum of min(b), where b ranges over every (contiguous) subarray of arr. Since the answer may be large, return the answer modulo 10^9 + 7.
1 <= arr.length <= 3 * 10^41 <= arr[i] <= 3 * 10^4
Example 1:
Input: arr = [3,1,2,4]
Output: 17
Explanation:
Subarrays are [3], [1], [2], [4], [3,1], [1,2], [2,4], [3,1,2], [1,2,4], [3,1,2,4].
Minimums are 3, 1, 2, 4, 1, 1, 2, 1, 1, 1.
Sum is 3 + 1 + 2 + 4 + 1 + 1 + 2 + 1 + 1 + 1 = 17
Example 2:
Input: arr = [11,81,94,43,3]
Output: 444
Solution
Approach: Monotonic Stack (Contribution Method)
The key insight is to think about the contribution of each element to the final sum, rather than iterating through all possible subarrays.
For each element arr[i], we need to count how many subarrays have arr[i] as their minimum value. If we can determine:
left[i]: the index of the first element to the left that is smaller thanarr[i]right[i]: the index of the first element to the right that is smaller than or equal toarr[i]
Then the number of subarrays where arr[i] is the minimum is:
count = (i - left[i]) × (right[i] - i)
The contribution of arr[i] to the final sum is:
contribution = arr[i] × count
Why use "smaller" on the left and "smaller or equal" on the right? This prevents double-counting when there are duplicate minimum values in a subarray.
Algorithm Steps:
- Use a monotonic increasing stack to find
left[i]for each element - Use another monotonic increasing stack to find
right[i]for each element - Calculate each element's contribution and sum them up
Time Complexity: O(n) - Each element is pushed and popped from the stack at most once Space Complexity: O(n) - For the left, right arrays and the stack
function sumSubarrayMins(arr: number[]): number {
const MOD = 10 ** 9 + 7;
const n = arr.length;
// left[i]: index of first smaller element to the left (-1 if none)
// right[i]: index of first smaller or equal element to the right (n if none)
const left: number[] = new Array(n).fill(-1);
const right: number[] = new Array(n).fill(n);
// Monotonic increasing stack to find left boundaries
const stack: number[] = [];
for (let i = 0; i < n; i++) {
while (stack.length > 0 && arr[stack[stack.length - 1]] >= arr[i]) {
stack.pop();
}
if (stack.length > 0) {
left[i] = stack[stack.length - 1];
}
stack.push(i);
}
// Clear stack and find right boundaries
stack.length = 0;
for (let i = n - 1; i >= 0; i--) {
while (stack.length > 0 && arr[stack[stack.length - 1]] > arr[i]) {
stack.pop();
}
if (stack.length > 0) {
right[i] = stack[stack.length - 1];
}
stack.push(i);
}
// Calculate the sum of contributions
let result = 0;
for (let i = 0; i < n; i++) {
const leftCount = i - left[i];
const rightCount = right[i] - i;
result = (result + arr[i] * leftCount * rightCount) % MOD;
}
return result;
}
Example Walkthrough
For arr = [3, 1, 2, 4]:
| Index | Value | left[i] | right[i] | leftCount | rightCount | Contribution |
|---|---|---|---|---|---|---|
| 0 | 3 | -1 | 1 | 1 | 1 | 3 × 1 × 1 = 3 |
| 1 | 1 | -1 | 4 | 2 | 3 | 1 × 2 × 3 = 6 |
| 2 | 2 | 1 | 4 | 1 | 2 | 2 × 1 × 2 = 4 |
| 3 | 4 | 2 | 4 | 1 | 1 | 4 × 1 × 1 = 4 |
Sum = 3 + 6 + 4 + 4 = 17 ✓