907. 子数组的最小值之和
题解:以 作为最小值,计算贡献的数值,需要注意数组中有重复的元素,需要去除重复计算, 严格小于 的位置, 是小于等于的 的位置, 的贡献是 , 使用单调栈来求 和 。
class Solution {
public:
int sumSubarrayMins(vector<int>& arr) {
int n = arr.size();
vector<long long> l(n, -1), r(n, n);
stack<int> st;
for(int i=0; i<n; i++){
while(!st.empty() && arr[st.top()] >= arr[i]){
r[st.top()] = i;
st.pop();
}
if(!st.empty()){
l[i] = st.top();
}
st.push(i);
}
long long res = 0;
int mod = 1e9+7;
for(int i=0; i<n; i++){
res += (i - l[i]) * (r[i] - i) * arr[i] % mod;
}
return (res+mod)%mod;
}
};
1856. 子数组最小乘积的最大值
单调栈 + 前缀和
class Solution {
public:
int maxSumMinProduct(vector<int>& nums) {
long long res = 0;
int n = nums.size();
// 左边是严格小于 nums[i], 右边非严格小于等于
vector<long long> l(n, -1), r(n, n);
stack<int> st;
for(int i=0; i<n; i++){
while(!st.empty() && nums[st.top()] >= nums[i]){
r[st.top()] = i;
st.pop();
}
if(!st.empty()) l[i] = st.top();
st.push(i);
}
vector<long long> f(n+1);
int mod = 1e9+7;
for(int i=1; i<=n; i++) f[i] = f[i-1] + nums[i-1];
for(int i=0; i<n; i++){
long long t = nums[i]*(f[r[i]] - f[l[i]+1]);
res = max(res, t);
}
return res%mod;
}
};
2104. 子数组范围和
题解:最大差值, 以 作为最大值的个数 , 作为最小值的个数为 , 贡献的数值为 , 即 , 枚举所有元素作为最大值之和 - 最小值之和。 使用 单调栈来求做为最大值的个数之和或者最小值个数之和。
class Solution {
public:
int n;
long long getMax(vector<int>& nums){
nums.push_back(1e9+1);
stack<int> st;
int m = n+1;
long long res = 0;
for(int i=0; i<m; i++){
while(!st.empty() && nums[st.top()] < nums[i]){
int idx = st.top();
st.pop();
int next = -1;
if(!st.empty()){
next = st.top();
}
res += ((long long)nums[idx]) * (i - idx) *(idx-next);
}
st.push(i);
}
return res;
}
long long subArrayRanges(vector<int>& nums) {
n = nums.size();
long long maxv = getMax(nums);
// 小技巧, 将数值反过来再求一般最大值,也就是最小值了。
for(int i=0; i<n; i++) nums[i] = - nums[i];
long long minv = getMax(nums); // 最小值是负数
return maxv+minv;
}
};
总结: 通过单调栈求左右两边的最值来计算贡献的数值,注意重复数据需要去除重复计算。