题目大意
原题面
给你一个数组 nums,然后进行 k 轮游戏,每轮游戏都会选择数组当中最小的元素然后乘上一个数 multiplier(题目给出),问你 k 轮游戏结束之后,这个数组长什么样子,所有的元素要对 1e9 + 7 取模。
1 ≤ n u m s . l e n g t h ≤ 1 0 4 1 ≤ n u m s [ i ] ≤ 1 0 9 1 ≤ k ≤ 1 0 9 1 ≤ m u l t i p l i e r ≤ 1 0 6 1 \le nums.length \le 10^4\\ 1 \le nums[i] \le 10^9\\ 1 \le k \le 10^9\\ 1 \le multiplier \le 10^6 1≤nums.length≤1041≤nums[i]≤1091≤k≤1091≤multiplier≤106
题解
我一看数据范围,第一反应是用矩阵快速幂,但是显然不现实,没思路怎么办?那么就不得不提到我个人最擅长的算法,打表+大眼球观察法。
我们就用题目给出的样例1进行举例。
nums = [2,1,3,5,6], k = 5, multiplier = 2
数组的变化如下:
我们发现在元素的选择上,貌似当数组达成了某些条件之后,元素的选择就会陷入循环。
当 k 非常大的时候,很显然整个数组都会变得 “差不多一样大”,因为每次对最小的元素进行一个乘法操作,当 k 非常大的时候,数组中所有的元素都会变成 multiplier 的某次方然后乘上一个很小的数,这个数就是题目中给出的数组中的元素(相对于乘上的数很小)。当我们继续选择一个元素去进行乘法的时候,这个元素就会变得非常的大,比其他所有元素都要大,只有所有的元素都完成了一次乘法操作之后,这个元素才有可能被再次做乘法。
那么这里我们就把 “某些条件” 找出来了,就是当数组中所有的元素全都满足做完乘法以后是当前数组中最大的元素,重复的选择就开始了。
这个时候我们把这个数组进行排序,此时的顺序,就是重复进行乘法操作的顺序。
例如上面这张图上的样例,当数组变成了 [8, 8, 6, 5, 6] 的时候,其中所有的元素都满足乘以 2 之后是数组的最大值,后面就开始进行了重复的乘法操作。然后就开始开始按照 1, 2, 4, 3, 5 的顺序进行选择。
代码
const int MOD = 1e9 + 7;struct num {int pos, w;bool operator <(const num& tmp) const{if (w == tmp.w)return pos < tmp.pos;return w < tmp.w;}
};struct Compare {bool operator()(const num& a, const num& b) const{if (a.w == b.w)return a.pos > b.pos;return a.w > b.w;}
};int solve(int x, long long multiplier ,int p) // 用来计算 x 进行 p 次乘法之后的结果
{long long ans = x;while (p){if (p & 1)ans *= multiplier, ans %= MOD;multiplier *= multiplier, p >>= 1, multiplier %= MOD;}return ans;
}class Solution {
public:vector<int> getFinalState(vector<int>& nums, int k, int multiplier){const int n = nums.size();if (multiplier == 1)return nums;vector<int> p(n + 5, 0);int cnt = 0, maxdata = 0;for (int i = 0; i < n; i++)maxdata = max(maxdata, nums[i]);vector<int> a = nums;int K = k;for (int i = 0; i < n; i++) // 先把数组变成所有元素乘二都会变成最大值的状态{while ((long long)a[i] * multiplier <= maxdata && K >= -1){a[i] *= multiplier;K--;p[i]++;}}if (K < 0) // 还没变成想要的状态就结束了,这个时候 k 一定很小,直接用优先队列模拟{priority_queue<num, vector<num>, Compare> q;for (int i = 0; i < n; i++){q.push(num{i, nums[i]});}for (int i = 1; i <= k; i++){num tmp = q.top();q.pop();nums[tmp.pos] *= multiplier;q.push(num{tmp.pos, nums[tmp.pos]});}}else // 不然就要开始进行重复的乘法操作{k = K;for (int i = 0; i < n; i++)p[i] += k / n; // 每个元素平均分配vector<num> q;for (int i = 0; i < n; i++) // 排序,此时的 a[] 满足我们想要的条件,后面的选择按照此时的大小顺序进行选择q.push_back(num {i, a[i]});sort(q.begin(), q.end());for (int i = 0; i < k % n; i++)p[q[i].pos]++;for (int i = 0; i < n; i++) // 求结果nums[i] = solve(nums[i], multiplier, p[i]);}return nums;}
};
我个人的数学水平不太好,希望这种说法能让大家理解!