# 2281.sum-of-total-strength-of-wizards

### 题目地址(2281. 巫师的总力量和)

https://leetcode.cn/problems/sum-of-total-strength-of-wizards/

### 题目描述

```
作为国王的统治者，你有一支巫师军队听你指挥。

给你一个下标从 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
```

### 前置知识

*

### 公司

* 暂无

### 思路

如果想做出来这道题，建议先做一下简化版的这道题：[907. 子数组的最小值之和](https://leetcode.cn/problems/sum-of-subarray-minimums/)

简单说一下上面那个简化版的题目。

一种思考方式是**计算每一个数组项 nums\[i]** 对结果的贡献 c\[i]，那么答案就是对 c\[i] 求和。

nums\[i] 对结果的贡献是包含 nums\[i] 的子数组，且该子数组的最小值是 nums\[i]。于是，我们可以分别找到 nums\[i] 左侧和右侧第一个比 nums\[i] 小的值 l 和 r，那么子数组 \[L,R] 就是一个符合要求的子数组。其中 L 范围是 \[l+1,i] R 范围是 \[i,r-1]。

根据笛卡尔积可知，每一项 a 对结果的贡献就是 `a * (i - l) * (r - i)`

而找到左侧（或者右侧）第一个比其大（或者小）的元素考虑使用单调栈。

参考代码：

```py
class Solution:
    def sumSubarrayMins(self, A: List[int]) -> int:
        n = len(A)
        st = []
        left = [-1] * n
        right = [n] * n
        res = 0
        for i, a in enumerate(A):
            while st and a <= A[st[-1]]:
                right[st.pop()] = i
            if st:
                left[i] = st[-1]
            st.append(i)
        for i, a in enumerate(A):
            res += a * (i - left[i]) * (right[i] - i)

        return res % 1000000007

```

对这道题来说，我们也需要知道左侧和右侧第一个比其小的，因此使用单调栈也可以解决。不同的是，我们需要求所有子数组和的和。

和前面一样子数组 \[L,R] 就是一个符合要求的子数组。其中 L 范围是 \[l+1,i] R 范围是 \[i,r-1]。

关键是每一项 a 对结果的贡献是多少呢？我们知道子数组和可以用前缀和来计算，只要知道左右端点即可求出。而这里有两个变量，一个是左边界，一个是右边界。

假设我们符合要求的子数组是 `l1,l2,l3,a,r1,r2,r3`不妨固定其中一个，以固定左边界为例。我们先固定 l1， 那么右边界就可以是 r1，r2,r3。此时的贡献是 s\[r3] - s\[l1]（即子 l1 到 r3 这一段的贡献）+ s\[r2] - s\[l1]（即子 l1 到 r2 这一段的贡献）+ s\[r1] - s\[l1]（即子 l1 到 r1 这一段的贡献）。类似的，我们需要固定 l2 和 l3 。 因此一共有 3 个 s\[l1]，3 就是 a 右侧元素个数 rn。

因此`每一项 a 对结果的贡献就是 a * (racc * ln - lacc * rn) % mod`

### 关键点

* 计算每一项对结果的贡献
* 固定一个变量

### 代码

* 语言支持：Python3

Python3 Code:

```python

class Solution:
    def totalStrength(self, A):
        mod = 10 ** 9 + 7
        n = len(A)

        right = [n] * n
        left = [-1] * n
        st = []
        for i in range(n):
            while st and A[st[-1]] >= A[i]:
                right[st.pop()] = i
            if st:
                left[i] = st[-1]
            st.append(i)

        res = 0
        acc = list(accumulate(accumulate(A), initial = 0))
        for i in range(n):
            l, r = left[i], right[i]
            lacc = acc[i] - acc[max(l, 0)]
            racc = acc[r] - acc[i]
            ln, rn = i - l, r - i
            res += A[i] * (racc * ln - lacc * rn) % mod
        return res % mod

```

**复杂度分析**

令 n 为数组长度。

* 时间复杂度：$O(n)$
* 空间复杂度：$O(n)$

### 参考

* [lee: Python Solution, O(n)](https://leetcode.com/problems/sum-of-total-strength-of-wizards/discuss/2061985/Python-Solution-O\(n\))

> 此题解由 [力扣刷题插件](https://leetcode-pp.github.io/leetcode-cheat/?tab=solution-template) 自动生成。

力扣的小伙伴可以[关注我](https://leetcode-cn.com/u/fe-lucifer/)，这样就会第一时间收到我的动态啦\~

以上就是本文的全部内容了。大家对此有何看法，欢迎给我留言，我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库：https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。

关注公众号力扣加加，努力用清晰直白的语言还原解题思路，并且有大量图解，手把手教你识别套路，高效刷题。

![](https://p.ipic.vip/6jaza9.jpg)
