力扣 2281. 巫师的总力量和 前缀和的前缀和

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 <= 10^5
  • 1 <= strength[i] <= 10^9

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/sum-of-total-strength-of-wizards
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法:前缀和的前缀和

我愿称之为力扣最难,看都看不懂那种。。。我看别人题解+问群友+写大概6个小时?

  1. 范围-单调栈解决,范围 [peek+1,i-1],当前位置:index
  2. 然后思考,这些范围内的数字,各自计算几次呢?

    • 假设 1 是最小值,旁边各范围内计算次数如图所示
    • 可以分为3部分,最小值左侧,最小值自己,最小值右侧
    • 对于左侧数字,计算次数总要包含 i-index ,然后从1次加到 index-peek-1 次
    • 对于右侧数字,计算次数总要包含 index-peek, 然后从1次加到 i-index-1 次
  3. 前缀和的前缀和,可以得到后半部分,如图(全部-紫色-黄色)

  4. 前缀和-前缀和的前缀和,可得前半部分(全部-紫色)

  5. (index-peek)*(i-index)*strength[index] 可得中间和的和
  6. 乘以最小值然后相加即可
  7. 各种求余数减法前都先((a+MOD)%MOD+MOD-b%MOD+MOD)%MOD,有减法减之前先求余数补MOD,减完再补MOD求余数
class Solution {
    public int totalStrength(int[] strength) {
        int n = strength.length;
        long []p1 = new long[n+1];
        long[] pp1 = new long[n+1];
        long MOD = (long) (1e9+7);
        for(int i = 0; i < n; i++){
//            p1[i+1] = (p1[i]+strength[i]);
//            pp1[i+1] = (p1[i+1]+pp1[i]);
            p1[i+1] = (p1[i]+strength[i])%MOD;
            pp1[i+1] = (p1[i+1]+pp1[i])%MOD;

        }

        Stack<Integer> stack = new Stack<>();
        stack.push(-1);
        long ans = 0;
        for(int i = 0; i <= n; i++){
            while (stack.peek()!=-1 && (i==n || strength[stack.peek()]>strength[i])){
                int index = stack.pop();
                int peek = stack.peek();
                long a = (index-peek)%MOD*(pp1[i]%MOD-pp1[index+1]%MOD+MOD-(i-index-1)*p1[index+1]%MOD)%MOD*strength[index]%MOD;
                 long b = (i-index)%MOD*((index-peek)*(p1[index]-p1[peek+1])%MOD+MOD-(
                        (pp1[index]%MOD-pp1[peek+1]%MOD+MOD)%MOD-(index-peek-1)*p1[peek+1]%MOD+MOD)%MOD
                        )%MOD*strength[index]%MOD;
                long c = (index-peek)%MOD*(i-index)%MOD*strength[index]%MOD*strength[index]%MOD;

//                long a = (index-peek)*(pp1[i]-pp1[index+1]-(i-index-1)*p1[index+1])*strength[index];
//                long b = (i-index)*((index-peek)*(p1[index]-p1[peek+1])-(
//                        index==peek+1?0:((pp1[index]-pp1[peek+1])-(index-peek-1)*p1[peek+1])
//                ))*strength[index];
//                long c = (index-peek)*(i-index)*strength[index]*strength[index];
                long v = (a+b%MOD+c)%MOD;

                ans += v%MOD;
                 ans = ans%MOD;
            }
            stack.push(i);
        }
        return (int) (ans%MOD);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值