【Leetcode】2281. Sum of Total Strength of Wizards

题目地址:

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

给定给一个长 n n n数组 A A A,对于每个子数组 a a a,求 f ( a ) = min ⁡ a × ∑ a f(a)=\min a\times \sum a f(a)=mina×a。求 ∑ a ( min ⁡ a × ∑ a ) \sum_a (\min a\times \sum a) a(mina×a)

A A A的前缀和数组为 p p p,对于 A [ k ] A[k] A[k],我们求出其右边第一个比其小的数 A [ j ] A[j] A[j](不存在则视为 j = n j=n j=n)和左边第一个比其小于等于的数 A [ i ] A[i] A[i](不存在则视为 i = − 1 i=-1 i=1),这两个数的位置可以用单调栈得出。那么 A [ k ] A[k] A[k]可以作为 A [ i : j ] A[i:j] A[i:j]的最小值。主要的问题是如何求 A [ i + 1 : j − 1 ] A[i+1:j-1] A[i+1:j1]的所有含 A [ k ] A[k] A[k]的子数组的和的总和。设 p p p的前缀和数组为 q q q q [ i ] q[i] q[i] p p p的前 i i i项的和,由于 p p p的长度是 n + 1 n+1 n+1 q q q的长度则会变为 n + 2 n+2 n+2),则含 A [ k ] A[k] A[k]的子数组的和的总和为: ( k − i ) ( q [ j + 1 ] − q [ k + 1 ] ) − ( j − k ) ( q [ k + 1 ] − q [ i + 1 ] ) (k-i)(q[j+1]-q[k+1]) - (j-k)(q[k+1]-q[i+1]) (ki)(q[j+1]q[k+1])(jk)(q[k+1]q[i+1])可以这么理解,这个和即为以 A [ k , k + 1 , . . . , j − 1 ] A[k,k+1,...,j-1] A[k,k+1,...,j1]为结尾的前缀和减去 A [ i , . . . , k − 1 ] A[i, ...,k-1] A[i,...,k1]结尾的前缀和的所有组合的总和,我们可以把被减数汇总,其会重复加 k − i k-i ki次,同理减数也可以汇总,其会重复加 j − k j-k jk次。代码如下:

class Solution {
 public:
  int totalStrength(vector<int>& v) {
    const int MOD = 1e9 + 7;
    int n = v.size();
    long sum = 0, ss[n + 2];
    memset(ss, 0, sizeof ss);
    for (int i = 1; i <= n; i++) {
      sum += v[i - 1];
      ss[i + 1] = (ss[i] + sum) % MOD;
    }
    int l[n], r[n];
    fill(l, l + n, 0);
    fill(r, r + n, n);
    stack<int> stk;
    for (int i = 0; i < n; i++) {
      while (stk.size() && v[stk.top()] > v[i]) {
        r[stk.top()] = i;
        stk.pop();
      }

      l[i] = stk.size() ? stk.top() : -1;
      stk.push(i);
    }

    int res = 0;
    for (int k = 0; k < n; k++) {
      sum = ((k - l[k]) * (ss[r[k] + 1] - ss[k + 1]) % MOD -
			(r[k] - k) * (ss[k + 1] - ss[l[k] + 1]) % MOD) % MOD;
      sum = (sum + MOD) % MOD;
      res = (res + sum * v[k]) % MOD;
    }

    return res;
  }
};

时空复杂度 O ( n ) O(n) O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值