子数组的最小值之和
方法一:单调栈
考虑所有满足以数组 arr\textit{arr}arr 中的某个元素 arr[i]\textit{arr}[i]arr[i] 为最右且最小的元素的子序列个数 C[i]C[i]C[i],那么题目要求求连续子数组的最小值之和即为 ∑i=0n−1arr[i]×C[i]\sum_{i=0}^{n-1} \limits \textit{arr}[i] \times C[i]
i=0
∑
n−1
arr[i]×C[i],其中数组 arr\textit{arr}arr 的长度为 nnn。我们必须假设当前元素为最右边且最小的元素,这样才可以构造互不相交的子序列,否则会出现多次计算,因为一个数组的最小值可能不唯一。
经过以上思考,我们只需要找到每个元素 arr[i]\textit{arr}[i]arr[i] 以该元素为最右且最小的子序列的数目 left[i]\textit{left}[i]left[i],以及以该元素为最左且最小的子序列的数目 right[i]\textit{right}[i]right[i],则以 arr[i]\textit{arr}[i]arr[i] 为最小元素的子序列的数目合计为 left[i]×right[i]\textit{left}[i] \times \textit{right[i]}left[i]×right[i]。对于数组中每个元素 arr[i]\textit{arr}[i]arr[i],具体做法如下:
求左边第一个小于 arr[i]\textit{arr}[i]arr[i] 的元素:从左向右遍历数组,并维护一个单调递增的栈,遍历当前元素 arr[i]\textit{arr}[i]arr[i],如果遇到当前栈顶的元素大于等于 arr[i]\textit{arr}[i]arr[i] 则将其弹出,直到栈顶的元素小于 arr[i]\textit{arr}[i]arr[i],栈顶的元素即为左边第一个小于 arr[i]\textit{arr}[i]arr[i] 的元素 arr[j]\textit{arr}[j]arr[j],此时 left[i]=i−j\textit{left}[i] = i - jleft[i]=i−j。
求右边第一个大于等于 arr[i]\textit{arr}[i]arr[i] 的元素:从右向左遍历数组,维护一个单调递增的栈,遍历当前元素 arr[i]\textit{arr}[i]arr[i],如果遇到当前栈顶的元素大于 arr[i]\textit{arr}[i]arr[i] 则将其弹出,直到栈顶的元素小于等于 arr[i]\textit{arr}[i]arr[i],栈顶的元素即为右边第一个小于等于 arr[i]\textit{arr}[i]arr[i] 的元素 arr[k]\textit{arr}[k]arr[k],此时 right[i]=k−i\textit{right}[i] = k - iright[i]=k−i。
连续子数组 arr[j],arr[j+1],⋯ ,arr[k]\textit{arr}[j], \textit{arr}[j + 1], \cdots, \textit{arr}[k]arr[j],arr[j+1],⋯,arr[k] 的最小元素即为 arr[i]\textit{arr}[i]arr[i],以 arr[i]\textit{arr}[i]arr[i] 为最小元素的连续子序列的数量为 (i−j)×(k−i)(i - j) \times (k - i)(i−j)×(k−i)。
arr[i]×left[i]×right[i]。维护单调栈的过程线性的,因为只进行了线性次的入栈和出栈。
MOD = 10 ** 9 + 7
class Solution:
def sumSubarrayMins(self, arr: List[int]) -> int:
n = len(arr)
monoStack = []
left = [0] * n
right = [0] * n
for i, x in enumerate(arr):
while monoStack and x <= arr[monoStack[-1]]:
monoStack.pop()
left[i] = i - (monoStack[-1] if monoStack else -1)
monoStack.append(i)
monoStack = []
for i in range(n - 1, -1, -1):
while monoStack and arr[i] < arr[monoStack[-1]]:
monoStack.pop()
right[i] = (monoStack[-1] if monoStack else n) - i
monoStack.append(i)
ans = 0
for l, r, x in zip(left, right, arr):
ans = (ans + l * r * x) % MOD
return ans