LeetCode #907 Medium

Sum of Subarray Minimums

Return the sum of the minimum element over every subarray, modulo 1e9+7.

Constraints
  • 1 <= arr.length <= 3 * 10⁴
  • 1 <= arr[i] <= 3 * 10⁴
monotonic-stackarraycontribution
Open on LeetCode ↗
02

Intuition

Sum of subarray minimums adds up the minimum of every contiguous subarray. There are O(n²) subarrays, so enumerating them is far too slow. The reframing that makes this tractable inverts the question. Instead of asking what is the minimum of each subarray, ask: - For each element, how many subarrays have it as their minimum? Then the answer is the sum of element × count over all elements. That count is determined by how far the element's influence extends. It is the minimum of every subarray that spans it without including anything smaller — so the boundaries are the nearest smaller element on each side. If the nearest smaller element to the left is at distance left and to the right at distance right, the element is the minimum of exactly left × right subarrays — one for each choice of start and end within its range. A monotonic increasing stack finds both boundaries in linear time, one pass per side, or both in a single pass with care. Duplicate values are the trap. If both sides use the same strict comparison, subarrays containing two equal minimums get counted twice. The fix is asymmetry: use strictly smaller on one side and smaller-or-equal on the other. That assigns each subarray to exactly one of its tied minimums. Getting that wrong produces answers that are correct on arrays with distinct values and wrong the moment duplicates appear — which is why it survives casual testing. The result requires modulo 10⁹+7, applied as the sum accumulates rather than at the end. Each element is pushed and popped once, giving O(n) time and O(n) space.

How to spot this pattern

Contribution counting. Instead of enumerating subarrays, ask how many of them each element is the minimum of — that's (i - pse) * (nse - i), the choices of left and right boundary within its span. The asymmetric strictness (>= on one side, > on the other) is what stops equal values double-counting.

03

Approach

Try it first

Before reading on: price up what the direct approach costs here, then ask what ordering you can maintain so the answer is always at one end. Aim for O(n) time and O(n) space.

1

Invert the question

Rather than finding each subarray's minimum, ask how many subarrays each element is the minimum of. The answer is then a weighted sum.

2

Find the nearest smaller elements

An element is the minimum of every subarray spanning it without anything smaller, so its boundaries are the nearest smaller element on each side.

3

Multiply the two distances

With left and right as the distances to those boundaries, the element is the minimum of exactly left × right subarrays — one per start and end choice.

4

Use a monotonic stack

An increasing stack finds both boundaries in linear time, popping elements as a smaller value arrives.

5

Break ties asymmetrically

Use strictly smaller on one side and smaller-or-equal on the other. Identical comparisons double-count subarrays with tied minimums.

6

Apply the modulus while summing

Take the result modulo 10^9+7 as it accumulates, not at the end — the running total overflows well before the largest inputs.

7

Cost of the approach

Each element is pushed and popped once, giving O(n) time and O(n) space in place of the O(n²) enumeration.

04

Solution & live demo

▶1class Solution:
▶2 def sumSubarrayMins(self, arr):
▶3 MOD = 10 ** 9 + 7
▶4 n = len(arr)
▶5 pse, nse = [-1] * n, [n] * n
▶6 st = []
▶7 for i in range(n):
▶8 while st and arr[st[-1]] >= arr[i]:
▶9 st.pop()
▶10 pse[i] = st[-1] if st else -1
▶11 st.append(i)
▶12 st = []
▶13 for i in range(n - 1, -1, -1):
▶14 while st and arr[st[-1]] > arr[i]:
▶15 st.pop()
▶16 nse[i] = st[-1] if st else n
▶17 st.append(i)
▶18 total = 0
▶19 for i in range(n):
▶20 total += arr[i] * (i - pse[i]) * (nse[i] - i)
▶21 return total % MOD
05

Common pitfalls

Using the same strictness in both passes

✗ Wrong
while st and arr[st[-1]] >= arr[i]:   # in both loops
✓ Right
# previous smaller: >=
# next smaller:     >

With duplicates, symmetric comparisons let two equal elements each claim the subarrays between them, so those are counted twice. Making one side strict assigns every subarray to exactly one representative.

Enumerating subarrays directly

✗ Wrong
for i in range(n):
    for j in range(i, n):
        total += min(arr[i:j+1])
✓ Right
total += arr[i] * (i - pse[i]) * (nse[i] - i)

That's O(n³), or O(n²) with a running minimum — both too slow at n = 30,000. Contribution counting is linear because each element's span is found once by the stack.

Applying the modulo only at the end

✗ Wrong
total += arr[i] * (i - pse[i]) * (nse[i] - i)
return total % MOD
✓ Right
total = (total + arr[i] * (i - pse[i]) % MOD * (nse[i] - i)) % MOD

Python's big integers tolerate it, but in C++/Java the product of a value and two spans near 30,000 overflows a 64-bit int across the whole sum. Reduce as you accumulate.

06

Edge cases

All elements equal

The strict/non-strict asymmetry is exactly what stops double counting here — the case worth hand-checking.

Strictly increasing array

Every element is the minimum only of subarrays starting at itself, so left is always 1.

Single element

One subarray, so the answer is that element.

Overflow

Sums exceed 64-bit range for large inputs, so apply the modulus as you accumulate.

07

Complexity

Time
O(n)
Space
O(n)
Two monotonic-stack passes plus a linear sum, replacing the O(n^2) subarray enumeration.