难度困难
作为国王的统治者,你有一支巫师军队听你指挥。
给你一个下标从 0 开始的整数数组 strength
,其中 strength[i]
表示第 i
位巫师的力量值。对于连续的一组巫师(也就是这些巫师的力量值是 strength
的 子数组),总力量 定义为以下两个值的 乘积 :
- 巫师中 最弱 的能力值。
- 组中所有巫师的个人力量值 之和 。
请你返回 所有 巫师组的 总 力量之和。由于答案可能很大,请将答案对 109 + 7
取余 后返回。
子数组 是一个数组里 非空 连续子序列。
示例 1:
输入:strength = [1,3,1,2]
输出:44
解释:以下是所有连续巫师组:
- [1] ,总力量值为 min ([1]) * sum ([1]) = 1 * 1 = 1
- [3] ,总力量值为 min ([3]) * sum ([3]) = 3 * 3 = 9
- [1] ,总力量值为 min ([1]) * sum ([1]) = 1 * 1 = 1
- [2] ,总力量值为 min ([2]) * sum ([2]) = 2 * 2 = 4
- [1,3] ,总力量值为 min ([1,3]) * sum ([1,3]) = 1 * 4 = 4
- [3,1] ,总力量值为 min ([3,1]) * sum ([3,1]) = 1 * 4 = 4
- [1,2] ,总力量值为 min ([1,2]) * sum ([1,2]) = 1 * 3 = 3
- [1,3,1] ,总力量值为 min ([1,3,1]) * sum ([1,3,1]) = 1 * 5 = 5
- [3,1,2] ,总力量值为 min ([3,1,2]) * sum ([3,1,2]) = 1 * 6 = 6
- [1,3,1,2] ,总力量值为 min ([1,3,1,2]) * sum ([1,3,1,2]) = 1 * 7 = 7
所有力量值之和为 1 + 9 + 1 + 4 + 4 + 4 + 3 + 5 + 6 + 7 = 44 。
示例 2:
输入:strength = [5,4,6]
输出:213
解释:以下是所有连续巫师组:
- [5] ,总力量值为 min ([5]) * sum ([5]) = 5 * 5 = 25
- [4] ,总力量值为 min ([4]) * sum ([4]) = 4 * 4 = 16
- [6] ,总力量值为 min ([6]) * sum ([6]) = 6 * 6 = 36
- [5,4] ,总力量值为 min ([5,4]) * sum ([5,4]) = 4 * 9 = 36
- [4,6] ,总力量值为 min ([4,6]) * sum ([4,6]) = 4 * 10 = 40
- [5,4,6] ,总力量值为 min ([5,4,6]) * sum ([5,4,6]) = 4 * 15 = 60
所有力量值之和为 25 + 16 + 36 + 36 + 40 + 60 = 213 。
提示:
1 <= strength.length <= 10^5
1 <= strength[i] <= 10^9
# 贡献法 + 单调栈
可以转化为每个元素作为最小值,计算出它是哪些子数组的最小值
当前下标为
-
设 为左边第一个小于 的元素下标
-
设 为右边第一个小于等于 的元素下标
为什么需要小于等于 呢?
例如:
若小于
-
时候的其中一个子数组:
-
时候的其中一个子数组:
重复,则结果会变大。
若小于等于
- 时候的其中一个子数组:
- 时候的其中一个子数组:
则不会出现重复的子数组
-
此时我们知道了子数组的最大范围 ,由于该范围内的最小值都相等,可以将最小值都提出来乘以子数组和的和
即:
所以如何计算子数组的和的和呢?
例如:此时最大范围数组 ,范围:
求包含 ()的子数组的和的和
可以归纳出:,其中 为当前 下标
如何求 呢?
例如:
此时
可以归纳出:,其中 为 的前缀和数组
子数组的和的和:
对结果的贡献值为:
class Solution { | |
int MOD = 1000000007; | |
public int totalStrength(int[] strength) { | |
int n = strength.length; | |
long[] s = new long[n + 1]; | |
for (int i = 0; i < n; i++) { | |
s[i + 1] = s[i] + strength[i]; | |
} | |
long[] ss = new long[n + 2]; | |
for (int i = 1; i <= n; i++) { | |
// 取模后下面相减之后结果可能为负数 | |
ss[i + 1] = (ss[i] + s[i]) % MOD; | |
} | |
Deque<Integer> st = new ArrayDeque<>(); | |
st.addFirst(-1); | |
long ans = 0; | |
for (int r = 0; r <= n; r++) { | |
int x = r == n ? Integer.MIN_VALUE : strength[r]; | |
while (st.size() > 1 && strength[st.peekFirst()] >= x) { | |
Integer i = st.pollFirst(); | |
Integer l = st.peekFirst(); | |
ans = (ans + strength[i] * ((ss[r + 1] - ss[i + 1]) * (i - l) % MOD - (ss[i + 1] - ss[l + 1]) * (r - i)) % MOD) % MOD; | |
} | |
st.addFirst(r); | |
} | |
// 防止溢出 | |
return (int) (ans + MOD) % MOD; | |
} | |
} |
贡献法单调栈