【前缀和】【单调栈】LeetCode2281:巫师的总力量和

2023-12-22 14:28

本文主要是介绍【前缀和】【单调栈】LeetCode2281:巫师的总力量和,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者推荐

map|动态规划|单调栈|LeetCode975:奇偶跳

涉及知识点

单调栈
C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频

题目

作为国王的统治者,你有一支巫师军队听你指挥。
给你一个下标从 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

单调栈+两个前缀和

时间复杂度: O(n)。

单调栈

枚举各数组的最小值。left 是从右向左第一个小于strength[i]的下标是left,如果不存在left是-1;right是从左向右第一个小于等于strength[i]的下标,如果不存在right为m_c。
如果一个子数组strength[li,ri]的最小值是strength[i],且strength[i]左边没有和它相等的值。则:
li的取值范围是:(left,i]
ri的取值范围是:[i,right)
我们可以这样计算这些子数组和的和。累加strength[i]它出现的次数。
无论li和ri取值什么,i都在子数组中,所以i出现:(i-left)
(right-i)次。
li为left+1时,ri无论取值什么,都包括strength[left+1]
li为left+1和left+2时,ri无论取值什么,都包括strength[left+2]

(left,i)中的元素分别出现:right-i次、(right-i)*2、(right-i)*3…

(i,right)中的元素,分别出现:…、(i-left)*2、(i-left)次

前缀和

vPreSum[i] 记录:strength[j]的和,j取值范围[0,i)。
vPreSum2[i]记录:strength[j]*(j+1)的和,j取值范围[0,i)。

代码

核心代码

template<int MOD = 1000000007>
class C1097Int
{
public:C1097Int(long long llData = 0) :m_iData(llData% MOD){}C1097Int  operator+(const C1097Int& o)const{return C1097Int(((long long)m_iData + o.m_iData) % MOD);}C1097Int& operator+=(const C1097Int& o){m_iData = ((long long)m_iData + o.m_iData) % MOD;return *this;}C1097Int& operator-=(const C1097Int& o){m_iData = (m_iData + MOD - o.m_iData) % MOD;return *this;}C1097Int  operator-(const C1097Int& o){return C1097Int((m_iData + MOD - o.m_iData) % MOD);}C1097Int  operator*(const C1097Int& o)const{return((long long)m_iData * o.m_iData) % MOD;}C1097Int& operator*=(const C1097Int& o){m_iData = ((long long)m_iData * o.m_iData) % MOD;return *this;}bool operator<(const C1097Int& o)const{return m_iData < o.m_iData;}C1097Int pow(long long n)const{C1097Int iRet = 1, iCur = *this;while (n){if (n & 1){iRet *= iCur;}iCur *= iCur;n >>= 1;}return iRet;}C1097Int PowNegative1()const{return pow(MOD - 2);}int ToInt()const{return m_iData;}
private:int m_iData = 0;;
};class CRangIndex
{
public:template<class _Pr>CRangIndex(const vector<int>& nums, _Pr CurIndexCmpStackTopIndex){m_c = nums.size();m_vLeft.assign(m_c, -1);m_vRight.assign(m_c, m_c);stack<int> sta;for (int i = 0; i < m_c; i++){while (sta.size() && (CurIndexCmpStackTopIndex(i, sta.top()))){m_vRight[sta.top()] = i;sta.pop();}if (sta.size()){m_vLeft[i] = sta.top();}sta.emplace(i);}}int m_c;vector<int> m_vLeft, m_vRight;//vLeft[i] 从右向左第一个小于nums[i] ;vRight[i] 是第一个小于等于nums[i]。
};template<class T = long long >
vector<T> CreatePreSum(const vector<int>& nums)
{vector<T> preSum;preSum.push_back(0);for (int i = 0; i < nums.size(); i++){preSum.push_back (preSum[i]+ nums[i]);}return preSum;
}class Solution {
public:int totalStrength(vector<int>& strength) {CRangIndex ri(strength, [&](int i1, int i2) {return strength[i1] <= strength[i2]; });auto vPreSum = CreatePreSum<C1097Int<>>(strength);vector<C1097Int<>> vPreSum2 = { 0 };for (int i = 0 ; i < ri.m_c ;  i++ ){const auto& n = strength[i];vPreSum2.emplace_back(vPreSum2.back() + C1097Int<>(n)*(i+1));}C1097Int<> biRet = 0;for (int i = 0; i < ri.m_c; i++){const int left = ri.m_vLeft[i];const int right = ri.m_vRight[i];const int leftLen = i - left;//左边界的可选范围(left,i]const int rightLen = right - i;//右边界的可选范围[i,right)//计算(left,i)的巫师和C1097Int<> leftTotal = vPreSum2[i] - vPreSum2[left + 1] - (vPreSum[i] - vPreSum[left + 1]) * (left+1);//计算(i,right)的巫师和C1097Int<> rightTotal = (vPreSum[right] - vPreSum[i + 1])*(right+1) - ( vPreSum2[right] - vPreSum2[i + 1] );C1097Int<> iTotal = C1097Int<>(strength[i]) * leftLen * rightLen;//计算以小标i为最弱力量的子数组C1097Int<> cur = leftTotal * rightLen + rightTotal * leftLen + iTotal;cur *= strength[i];biRet += cur;}return biRet.ToInt();}
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{assert(t1 == t2);
}template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){Assert(v1[i], v2[i]);}
}int main()
{vector<int> strength;{Solution slu;strength = { 2,3,4 };auto res = slu.totalStrength(strength);Assert(78, res);}{Solution slu;strength = { 1 };auto res = slu.totalStrength(strength);Assert(1, res);}{Solution slu;strength = { 1,3 };auto res = slu.totalStrength(strength);Assert(14, res);}{Solution slu;strength = { 1, 3, 1, 2 };auto res = slu.totalStrength(strength);Assert(44, res);}{Solution slu;strength = { 5,4,6 };auto res = slu.totalStrength(strength);Assert(213, res);}{Solution slu;strength.assign(100'000, 1000'000'000);auto res = slu.totalStrength(strength);Assert(611131623, res);}//CConsole::Out(res);
}

2023年3月版

class Solution {
public:
int totalStrength(vector& strength) {
m_c = strength.size();
vector vSum1(1), vSum2(1);
for (int i = 0; i < m_c; i++)
{
vSum1.push_back(vSum1.back() + strength[i]);
vSum2.push_back(vSum2.back() + (long long)strength[i] * (i + 1));
}
vector vLeft(m_c), vRight(m_c);
{
std::vector<std::pair<int, int>> vValueIndex;
for (int i = 0; i < m_c; i++)
{
const int iLessEqualNum = std::lower_bound(vValueIndex.begin(), vValueIndex.end(), strength[i] + 1, LessPairInt) - vValueIndex.begin();
vLeft[i] = (0 == iLessEqualNum) ? -1 : vValueIndex[iLessEqualNum - 1].second;
while (vValueIndex.size() && (vValueIndex.back().first >= strength[i]))
{
vValueIndex.pop_back();
}
vValueIndex.emplace_back(strength[i], i);
}
}
{
std::vector<std::pair<int, int>> vValueIndex;
for (int i = m_c - 1; i >= 0; i–)
{
const int iLessNum = std::lower_bound(vValueIndex.begin(), vValueIndex.end(), strength[i], LessPairInt) - vValueIndex.begin();
vRight[i] = (0 == iLessNum) ? m_c : vValueIndex[iLessNum - 1].second;
while (vValueIndex.size() && (vValueIndex.back().first >= strength[i]))
{
vValueIndex.pop_back();
}
vValueIndex.emplace_back(strength[i], i);
}
}

	 C1097Int llSum = 0;for (int i = 0; i < m_c; i++){const int iLeftLeft = vLeft[i] + 1;const int iLeftRight = i - 1;const int iRightLeft = i + 1;const int iRightRight = vRight[i] - 1;const int iLeftLen = iLeftRight - iLeftLeft + 1;const int iRightLen = iRightRight - iRightLeft + 1;C1097Int llCurSun = ((long long)iLeftLen + 1)*(iRightLen + 1)*strength[i];C1097Int llLeftSum = 0, llRightSum = 0;if (iLeftLen > 0){llLeftSum = vSum2[iLeftRight + 1] - vSum2[iLeftLeft] - (vSum1[iLeftRight + 1] - vSum1[iLeftLeft])*iLeftLeft;llLeftSum *= (iRightLen + 1);}if (iRightLen > 0){llRightSum = (vSum1[iRightRight + 1] - vSum1[iRightLeft])*(iRightRight + 2) - (vSum2[iRightRight + 1] - vSum2[iRightLeft]);llRightSum *= (iLeftLen + 1);}llCurSun += llLeftSum;llCurSun += llRightSum;llSum += llCurSun*strength[i];}return llSum.ToInt();}int m_c;

};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关

下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法C++ 实现。

这篇关于【前缀和】【单调栈】LeetCode2281:巫师的总力量和的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/524282

相关文章

POJ1631最长单调递增子序列

最长单调递增子序列 import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;import java.math.BigInteger;import java.util.StringTokenizer;publ

Science Robotics 首尔国立大学研究团队推出BBEX外骨骼,实现多维力量支持!

重复性举起物体可能会对脊柱和背部肌肉造成损伤,由此引发的腰椎损伤是工业环境等工作场所中一个普遍且令人关注的问题。为了减轻这类伤害,有研究人员已经研发出在举起任务中为工人提供辅助的背部支撑装置。然而,现有的这类装置通常无法在非对称性的举重过程中提供多维度的力量支持。此外,针对整个人体脊柱的设备安全性验证也一直是一个缺失的环节。 据探索前沿科技边界,传递前沿科技成果的X-robot投稿,来自首尔国立

【LeetCode热题100】前缀和

这篇博客共记录了8道前缀和算法相关的题目,分别是:【模版】前缀和、【模版】二维前缀和、寻找数组的中心下标、除自身以外数组的乘积、和为K的子数组、和可被K整除的子数组、连续数组、矩阵区域和。 #include <iostream>#include <vector>using namespace std;int main() {//1. 读取数据int n = 0, q = 0;ci

AI 与大模型:物流行业的变革力量

一、物流行业的现状与挑战 物流行业在现代经济中扮演着至关重要的角色,但目前也面临着诸多挑战。 在效率方面,交通拥堵是一个突出问题。许多城市道路容量不足,无法满足日益增长的货物运输需求,导致运输时间延长。例如,在一些大城市,货物运输常常因交通拥堵而延迟,影响了整个供应链的效率。此外,信息不对称也严重影响了物流效率。供应商和购买方之间缺乏实时信息共享平台,双方无法准确了解货物的到达时间、配送状

力扣 739. 每日温度【经典单调栈题目】

1. 题目 理解题意: 1.1. 给一个温度集合, 要返回一个对应长度的结果集合, 这个结果集合里面的元素 i 是 当前 i 位置的元素的下一个更高温度的元素的位置和当前 i 位置的距离之差, 若是当前元素不存在下一个更高温度的元素, 则这个位置用0代替; 2. 思路 本题用单调栈来求解;单调栈就适用于来求当前元素左边或者右边第一个比当前元素大或者小的元素;【单调栈:让栈中的元素保持单调

前缀和 — 利用前缀信息解决子数组问题

【前缀和的核心思想是预先处理数组来快速计算任意子数组的和,基本上用于数组和序列问题。】 前缀和算法具体步骤 构造前缀和数组: 给定一个数组nums,其前缀和数组prex定义为prex[i]表示为数组nums从起始位置到第i个位置的元素累加和。构建前缀和公式: p r e x [ i ] = n u m s [ i ] ( i = = 0 ) p r e x [ i ] = p r e x

Redis 管道的神奇力量

今天我们要来探索一个 Redis 中非常强大且实用的特性——管道(Pipeline)。如果你想让你的 Redis 操作更加高效,那么这篇文章绝对值得一读。 一、Redis 管道是什么 Redis 管道是一种在客户端和服务器之间批量执行命令的技术。它允许客户端将多个命令一次性发送到服务器,而不是逐个发送并等待每个命令的响应。服务器会按照顺序执行这些命令,并将所有命令的响应一次性返回给客户端。

Differential Diffusion,赋予每个像素它应有的力量,以及在comfyui中的测试效果

🥽原论文要点 首先是原论文地址:https://differential-diffusion.github.io/paper.pdf 其次是git介绍地址:GitHub - exx8/differential-diffusion 感兴趣的朋友们可以自行阅读。 首先,论文开篇就给了一个例子: 我们的方法根据给定的图片和文本提示,以不同的程度改变图像的不同区域。这种可控性允许我们再现

探索Python的数学魔法:Numpy库的神秘力量

文章目录 探索Python的数学魔法:Numpy库的神秘力量背景:为什么选择Numpy?Numpy是什么?如何安装Numpy?五个简单的库函数使用方法场景应用常见Bug及解决方案总结 探索Python的数学魔法:Numpy库的神秘力量 背景:为什么选择Numpy? 在Python的世界中,数据处理和科学计算是不可或缺的一部分。但原生Python在处理大规模数据时可能会显

双指针(5)_单调性_有效三角形的个数

个人主页:C++忠实粉丝 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创 双指针(5)_单调性_有效三角形的个数 收录于专栏【经典算法练习】 本专栏旨在分享学习C++的一点学习笔记,欢迎大家在评论区交流讨论💌 目录 1. 题目链接: 2.题目描述 : 3.解法 :     解法一(暴力枚举) :     算法思路 :     代码展示 : 暴力枚