根据题意,使用模拟解法,维护一个最小堆,始终对堆的第一个元素做乘,然后每次运算后维护堆。在实现的时候保存原有的下标,可以很方便的输出答案,有实现如下:
class Solution {
public:
vector<int> getFinalState(vector<int>& nums, int k, int multiplier) {
int MOD = 1e9 + 7;
vector<int> res = nums;
if(multiplier == 1)
return res;
vector<pair<long long, int>> h;
for(int i = 0; i < res.size(); i++)
{
h.emplace_back(make_pair(nums[i], i));
}
make_heap(h.begin(), h.end(), greater<>());
while(k)
{
pop_heap(h.begin(), h.end(), greater<>());
h.back().first *= multiplier;
push_heap(h.begin(), h.end(), greater<>());
k--;
}
for(int i = 0; i < nums.size(); i++)
{
res[h[i].second] = h[i].first % MOD;
}
return res;
}
};
但是这样实现有一个问题,有时候计算堆中的元素会超过int甚至是long long型的最大值,而直接在循环里加入取模运算的话,会导致混乱。
为了避免这个问题,采取两步计算策略。首先用模拟的方法,计算答案;之后,当堆顶元素大于等于原nums
中最大值temMax
时,直接用数学公式计算。
因为对第一步计算完成后的堆排序后,假如用模拟的思路去计算,只是依次计算,如对下标为0的元素做乘,之后轮到下标为1的元素,直到最后,然后不断循环。这是因为在不取mod的情况下,h[i] < h[i + 1]
并且h[i] * multiplier > h[h.size()]
再使用快速幂加速,有实现:
class Solution {
public:
long long MOD = 1e9 + 7;
vector<int> getFinalState(vector<int>& nums, int k, int multiplier) {
vector<int> res = nums;
if(multiplier == 1)
return res;
vector<pair<long long, int>> h;
for(int i = 0; i < res.size(); i++)
{
h.emplace_back(make_pair(nums[i], i));
}
make_heap(h.begin(), h.end(), greater<>());
int temMax = ranges::max(nums);
while(k && h[0].first < temMax)
{
pop_heap(h.begin(), h.end(), greater<>());
h.back().first *= multiplier;
push_heap(h.begin(), h.end(), greater<>());
k--;
}
sort(h.begin(), h.end());
int t = k / nums.size();
for(int i = 0; i < nums.size(); i++)
{
if(k % nums.size())
{
k--;
res[h[i].second] = (h[i].first % MOD) * quickPow(multiplier, t + 1) % MOD;
}
else
res[h[i].second] = (h[i].first % MOD) * quickPow(multiplier, t) % MOD;
}
return res;
}
long long quickPow(long long m, long long t)
{
long long res = 1;
while(t)
{
if(t & 1)
res = (res * m) % MOD;
t >>= 1;
m = (m * m) % MOD;
}
return res;
}
};
标签:力扣,nums,int,res,multiplier,long,II,3266,MOD
From: https://www.cnblogs.com/yuhixyui/p/18605468