Algorithm

Sum of Subarray Minimums

Monotonic Stack Pattern

Sum of Subarray Minimums

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⁹ + 7.

CONSTRAINTS
  • 1 <= arr.length <= 3 × 10⁴
  • 1 <= arr[i] <= 3 × 10⁴
EXAMPLE 1
Input: arr = [3,1,2,4]
Output: 17
Subarrays: [3],[1],[2],[4],[3,1],[1,2],[2,4],[3,1,2],[1,2,4],[3,1,2,4]. Mins: 3,1,2,4,1,1,2,1,1,1. Sum = 17.
What should we used for the neighbors if they don't exist?
To simplify the math, we imagine virtual neighbors at index -1 and index N that are smaller than every possible value in the array.

To understand the optimal solution, we must first see why the natural approach fails as the array grows.

Brute Force (O(N²) Time, O(1) Space)

The most direct way is to look at every possible contiguous subarray, find its minimum, and add it to a running total.

python
total_sum = 0
n = len(arr)
for i in range(n):
    min_val = arr[i]
    for j in range(i, n):
        # Update minimum for current subarray arr[i...j]
        min_val = min(min_val, arr[j])
        total_sum += min_val
return total_sum % (10**9 + 7)

With an array of size 30,000, there are nearly 450 million subarrays. This quadratic approach is far too slow. To solve this efficiently, we must move from a "Subarray-First" mindset to an "Element-First" mindset.

The Inversion Discovery: From Subarrays to Elements

Instead of iterating through subarrays, we ask a different question for every single number in the array:
"In how many subarrays am I the absolute minimum?"

If a number X is the minimum for K different subarrays, then its total contribution to the final sum is simply X * K. By calculating this for every element, we get our answer in linear time.

The "Star of the Show" (Range of Influence)

Imagine you are a number at index i. You are the "star" (the minimum) of a subarray as long as every other number in that subarray is larger than you.
- Left Boundary (L): How many steps can you look to the left before hitting a number smaller than you?
- Right Boundary (R): How many steps can you look to the right before hitting a number smaller than you?

If you can extend L positions to the left (including yourself) and R positions to the right (including yourself), the number of subarrays where you are the minimum is:
Count = L * R

Why L * R?
Think of it as choosing a start and end point. You have L choices for where the subarray can start (anywhere from your left boundary to your current position) and R choices for where it can end (anywhere from your current position to your right boundary). Every combination of these start and end points creates a unique subarray where you are the minimum.

The Problem of Equality (Tie-Breaking)

What if the array has duplicates, like [1, 2, 1]? Both 1s will try to claim the entire array as their "Range of Influence," leading to double-counting.

To fix this, we use a Tie-Breaking Rule:
- An element claims a range to its left if the neighbors are strictly greater (>).
- An element claims a range to its right if the neighbors are greater than or equal (>=).

By being "strict" on one side and "loose" on the other, we ensure that every subarray has exactly one "owner," even if it contains multiple identical minimum values.

Solving with Monotonic Stacks

Finding the "nearest smaller neighbor" is the classic use case for a Monotonic Stack.
1. First Pass (Left): Find the distance to the previous smaller element for every index.
2. Second Pass (Right): Find the distance to the next smaller (or equal) element for every index.
3. Calculation: Multiply Value LeftDistance RightDistance and sum them up.

Code Blueprint
text
left_distance = [0] * n
right_distance = [0] * n
stack = []

// 1. Find Distance to Previous Less Element
FOR i from 0 to n-1:
    WHILE stack is NOT empty AND arr[stack.PEEK()] > arr[i]:
        stack.POP()
    left_distance[i] = i - (stack.PEEK() IF stack else -1)
    stack.PUSH(i)

stack.CLEAR()

// 2. Find Distance to Next Less Element (or Equal)
FOR i from n-1 down to 0:
    WHILE stack is NOT empty AND arr[stack.PEEK()] >= arr[i]:
        stack.POP()
    right_distance[i] = (stack.PEEK() IF stack else n) - i
    stack.PUSH(i)

// 3. Sum contributions
sum = 0
FOR i from 0 to n-1:
    sum += arr[i] * left_distance[i] * right_distance[i]
    sum %= MOD

RETURN sum
Worked Example:[3, 1, 2]
0
3
i = 0 (val 3)
1
1
2
2
Val 3: Left limit (PLE) index -1, Right limit (NLE) index 1. Subarrays where 3 is min: [3]. Contrib = 3 * (0 - (-1)) * (1 - 0) = 3 * 1 * 1 = 3.
0
3
1
1
i = 1 (val 1)
2
2
Val 1: Left limit -1, Right limit 3. Subarrays: [3,1], [1], [3,1,2], [1,2]. Contrib = 1 * (1 - (-1)) * (3 - 1) = 1 * 2 * 2 = 4.
0
3
1
1
2
2
i = 2 (val 2)
Val 2: Left limit 1, Right limit 3. Subarrays: [2]. Contrib = 2 * (2 - 1) * (3 - 2) = 2 * 1 * 1 = 2. Total sum = 3 + 4 + 2 = 9.
Interactive Strategy Visualization
CONTRIBUTION ANALYSIS ENGINE

Monotonic Stack Range Strategy

3
1
2
4

Mental Model

  • Contribution: Instead of summing subarrays (O(N²)), we ask: "How many subarrays is index `i` the minimum of?"
  • Expansion: We look left and right for nearest smaller elements using a Monotonic Stack.
LOGICSTEP 1/6
For each element, we find the range where it is the minimum.
STRATEGY

Avoid Double Counting

To handle duplicates, use ">" on one side and ">=" on the other. This ensures each subarray is assigned exactly one minimum index.

O(N²) Brute Force
O(N) Monotonic Stack