1856. 子数组最小乘积的最大值

难度中等

一个数组的 最小乘积 定义为这个数组中 最小值 乘以 数组的

  • 比方说,数组 [3,2,5] (最小值是 2 )的最小乘积为 2 * (3+2+5) = 2 * 10 = 20

给你一个正整数数组 nums ,请你返回 nums 任意 非空子数组最小乘积最大值 。由于答案可能很大,请你返回答案对 109 + 7 取余 的结果。

请注意,最小乘积的最大值考虑的是取余操作 之前 的结果。题目保证最小乘积的最大值在 不取余 的情况下可以用 64 位有符号整数 保存。

子数组 定义为一个数组的 连续 部分。

示例 1:

输入:nums = [1,2,3,2]

输出:14

解释:最小乘积的最大值由子数组 [2,3,2] (最小值是 2)得到。

2 * (2+3+2) = 2 * 7 = 14 。

示例 2:

输入:nums = [2,3,3,1,2]

输出:18

解释:最小乘积的最大值由子数组 [3,3] (最小值是 3)得到。

3 * (3+3) = 3 * 6 = 18 。

示例 3:

输入:nums = [3,1,5,6,4,2]

输出:60

解释:最小乘积的最大值由子数组 [5,6,4] (最小值是 4)得到。

4 * (5+6+4) = 4 * 15 = 60 。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 107

# 贡献法 + 单调栈

子数组若最小值相等,和肯定要越大越好

可以枚举最小值,计算出它是哪些子数组的最小值

当前下标 ii,往左边寻找第一个小于 nums[i]nums[i] 的元素下标 ll,往右边寻找第一个小于等于或者小于 nums[i]nums[i] 的元素下标 rr

为什么小于等于或者小于都可以?

例如:[2,5,4,2,3,5,3,1,2,4][2,5,4,2,3,5,3,1,2,4]

小于

  • 此时 i=0(num=2)i=0\,(num = 2) 的子数组:[2,5,4,2,3,5,3][2,5,4,2,3,5,3]
  • 此时 i=3(num=2)i=3\,(num=2) 的子数组:[2,5,4,2,3,5,3][2,5,4,2,3,5,3]

一样的


主要在于小于等于:

  • 此时 i=0(num=2)i=0\,(num=2) 的子数组:[2,5,4][2,5,4]
  • 此时 i=3(num=2)i=3\,(num=2) 的子数组:[2,5,4,2,3,5,3][2,5,4,2,3,5,3]

不影响最终的结果

class Solution {
    int MOD = 1000000007;
    public int maxSumMinProduct(int[] nums) {
        // 子数组若最小值相等,和肯定要越大越好
        // 当前下标 i,
        //  - 需要往左边寻找第一个大于 nums [i] 的元素下标 l
        //  - 需要往右边寻找第一个大于等于 nums [i] 的元素下标 r
        // 此时对答案的贡献值为 nums [i] * sum (l, r)
        int n = nums.length;
        // 前缀和
        long[] s = new long[n + 1];
        for (int i = 0; i < n; i++) {
            s[i + 1] = s[i] + nums[i];
        }
        // 存放的是递增的元素
        Deque<Integer> st = new LinkedList<>();
        st.addFirst(-1);
        long ans = 0;
        for (int r = 0; r <= n; r++) {
            // 说明还有剩余元素,此时内部都是递增的,其右边界为 n,需要都满足条件
            int x = r == n ? -1 : nums[r];
            while (st.size() > 1 && nums[st.peekFirst()] >= x) {
                // 此时弹出的栈顶元素 poll 右边第一个小于等于 poll 的元素为 r
                Integer i = st.pollFirst();
                // 此时栈顶弹出的 poll 下面的元素正好是 poll 的左边界
                int l = st.peekFirst();
                System.out.println("l:" + l + " i:" + i + " r:" + r);
   ans = Math.max(ans, nums[i] * (s[r] - s[l + 1]));
            }
            st.addFirst(r);
        }
        return (int) (ans % MOD);
    }
}