2281-巫师的总力量和
作为国王的统治者,你有一支巫师军队听你指挥。
给你一个下标从 0 开始的整数数组 strength
,其中 strength[i]
表示第 i
位巫师的力量值。对于连续的一组巫师(也就是这些巫师的力量值是 strength
的 子数组 ), 总力量 定义为以下两个值的
乘积 :
- 巫师中 最弱 的能力值。
- 组中所有巫师的个人力量值 之和 。
请你返回 所有 巫师组的 总 力量之和。由于答案可能很大,请将答案对 109 + 7
取余 后返回。
子数组 是一个数组里 非空 连续子序列。
示例 1:
**输入:** strength = [1,3,1,2]
**输出:** 44
**解释:** 以下是所有连续巫师组:
- [ _ **1**_ ,3,1,2] 中 [1] ,总力量值为 min([1]) * sum([1]) = 1 * 1 = 1
- [1, _ **3**_ ,1,2] 中 [3] ,总力量值为 min([3]) * sum([3]) = 3 * 3 = 9
- [1,3, _ **1**_ ,2] 中 [1] ,总力量值为 min([1]) * sum([1]) = 1 * 1 = 1
- [1,3,1, _ **2**_ ] 中 [2] ,总力量值为 min([2]) * sum([2]) = 2 * 2 = 4
- [ _ **1,3**_ ,1,2] 中 [1,3] ,总力量值为 min([1,3]) * sum([1,3]) = 1 * 4 = 4
- [1, _ **3,1**_ ,2] 中 [3,1] ,总力量值为 min([3,1]) * sum([3,1]) = 1 * 4 = 4
- [1,3, _ **1,2**_ ] 中 [1,2] ,总力量值为 min([1,2]) * sum([1,2]) = 1 * 3 = 3
- [ _ **1,3,1**_ ,2] 中 [1,3,1] ,总力量值为 min([1,3,1]) * sum([1,3,1]) = 1 * 5 = 5
- [1, _ **3,1,2**_ ] 中 [3,1,2] ,总力量值为 min([3,1,2]) * sum([3,1,2]) = 1 * 6 = 6
- [ _ **1,3,1,2**_ ] 中 [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**_ ,4,6] 中 [5] ,总力量值为 min([5]) * sum([5]) = 5 * 5 = 25
- [5, _ **4**_ ,6] 中 [4] ,总力量值为 min([4]) * sum([4]) = 4 * 4 = 16
- [5,4, _ **6**_ ] 中 [6] ,总力量值为 min([6]) * sum([6]) = 6 * 6 = 36
- [ _ **5,4**_ ,6] 中 [5,4] ,总力量值为 min([5,4]) * sum([5,4]) = 4 * 9 = 36
- [5, _ **4,6**_ ] 中 [4,6] ,总力量值为 min([4,6]) * sum([4,6]) = 4 * 10 = 40
- [ _ **5,4,6**_ ] 中 [5,4,6] ,总力量值为 min([5,4,6]) * sum([5,4,6]) = 4 * 15 = 60
所有力量值之和为 25 + 16 + 36 + 36 + 40 + 60 = 213 。
提示:
1 <= strength.length <= 105
1 <= strength[i] <= 109
本题 视频讲解 已出炉,额外介绍了单调栈的原理,欢迎点赞三连~
提示 1-1
枚举每位巫师,假设他是最弱的巫师,那么他能在哪些子数组中?
提示 1-2
左右边界最远能到哪?具体地,这些子数组的左边界的最小值是多少,右边界的最大值是多少?
提示 1-3
用单调栈来计算左右边界。
不了解单调栈的同学可以看一下 496. 下一个更大元素 I 。对于本题,我们需要求的是下一个更小元素。
提示 1-4
注意本题是可能有重复元素的,这会对最终答案的计算产生什么影响?
提示 1-5
设左右边界为 [L,R]。
为了避免重复计算,我们可以考虑左侧严格小于当前元素的最近元素位置 L-1,以及右侧小于等于当前元素的最近元素位置 R+1。
以示例 1 中的数组 [1,3,1,2] 为例,如果左右两侧都是找严格小于,那么第一个 1 和第二个 1 算出来的边界范围都是一样的(都是整个数组),这就重复统计了,为了避免这种情况,可以把某一侧改为小于等于,比如把右侧改成小于等于,那么第一个 1 算出来的右边界不会触及或越过第二个 1,这样就能避免重复统计同一个子数组。
提示 2-1
设当前枚举的巫师的能力值为 v,那么他对答案产生的贡献是 v 乘上在左右边界 [L,R] 内的所有包含 v 的子数组的元素和的和。
提示 2-2
如何计算子数组的元素和?
用前缀和来计算。
提示 2-3
如何计算子数组的元素和的和?
不妨将子数组的右端点固定,子数组左端点的范围是多少?
对于多个不同的右端点,其对应的左端点的范围是否均相同?
提示 2-4
设子数组左端点为 l,右端点为 r,当前枚举的元素下标为 i,那么有 L\le l\le i \le r\le R。
设 strength 数组的前缀和为 s,其中 s[i]=\sum\limits_{j=0}^{i-1} \textit{strength}[j],因此子数组 [l,r] 的元素和可以表示为
s[r+1]-s[l]
在范围 [L,R] 内的所有子数组的元素和的和可以表示为
\begin{aligned}
&\sum_{r=i+1}^{R+1}\sum_{l=L}^{i} (s[r]-s[l]) \
=&\left(\sum_{r=i+1}^{R+1}\sum_{l=L}^{i} s[r]\right)-\left(\sum_{r=i+1}^{R+1}\sum_{l=L}^{i} s[l]\right) \
=&(i-L+1)\cdot \sum_{r=i+1}^{R+1}s[r] -(R-i+1)\cdot \sum_{l=L}^{i} s[l]
\end{aligned}
因此我们还需要计算出前缀和 s 的前缀和 ss,其中 ss}[i]=\sum\limits_{j=0}^{i-1}s[j],上式即为
(i-L+1)\cdot (\textit{ss}[R+2]-\textit{ss}[i+1]) - (R-i+1)\cdot (\textit{ss}[i+1]-\textit{ss}[L])
再乘上 v 即为当前巫师的贡献,累加所有贡献即为答案。
1 | class Solution: |
1 | class Solution { |
1 | class Solution { |
1 | func totalStrength(strength []int) (ans int) { |
复杂度分析
- 时间复杂度:O(n)。
- 空间复杂度:O(n)。