六月集训(第08天)—前缀和
前缀和
1. 1838. 最高频元素的频数
思路:
枚举每个数字作为频数最大元素时对应的情况。
(1) 对数组排序
(2) 枚举每个元素作为频数最大的元素时,k次操作最多能将多少个元素变成当前元素。显然,排序后元素的选取应该是从频数最大的元素往前选取。
(3) 得到每个元素作为最大元素时对应的频数,更新答案。
class Solution {
public:
int maxFrequency(vector<int>& nums, int k) {
long long nums_size = nums.size(), i, j;
long long sum[nums_size + 1];
int ans = 0;
sort(nums.begin(), nums.end());
memset(sum, 0, sizeof(sum));
for (i = 1; i <= nums_size; ++i) { // 令sum[0] = 0; 可求出 nums[0] = sum[1] - sum[0]; 方便一些
sum[i] = sum[i - 1] + nums[i - 1];
}
j = 0;
for (i = 1; i <= nums_size; ++i) { // 枚举每一位作为频数最高的数
long long s = ((long long)nums[i - 1]) * (i - j) - (sum[i] - sum[j]);
while (s > k) { // 以当前数作为频数最高的数,将所有比它小的数变为当前数,如果操作数不够用,舍弃对最小的数的操作。反复如此直到操作数够用
j++;
s = ((long long)nums[i - 1] * (i - j) - (sum[i] - sum[j]));
}
ans = ans > (i - j) ? ans : (i - j);
}
return ans;
}
};
2. 1590. 使数组和能被 P 整除
思路:
利用同余定理: $(sum[n] - (sum[i] - sum[j]))$ % p == 0;即$sum[i] - sum[n]$与$sum[j]$同余,从i = 0到i = n枚举$sum[i] - sum[n]$,并建立前缀和的hash映射,可直接在hash表中查找需要的$sum[j]$,更新答案即可。
class Solution {
#define ll long long
public:
int minSubarray(vector<int>& nums, int p) {
int nums_size = nums.size();
ll sum = 0;
int i;
int ret = nums_size;
unordered_map<ll, int> hash;
for (i = 0; i < nums_size; ++i) sum += (ll)nums[i];
ll mod = sum % (ll)p;
if (mod == 0) return 0;
hash[0ll] = 0; /* 前缀和数组sum[0] = 0,此处做一个补充修正 */
sum = 0;
for (i = 1; i <= nums_size; ++i) {
sum += nums[i - 1];
sum %= (ll)p;
ll target_mod = (sum - mod + (ll)p) % (ll)p;
if (hash.find(target_mod) != hash.end()) ret = min(ret, i - hash[target_mod]); // 更新答案
hash[sum] = i; // 不断更新同模的前缀和的位置,确保去掉的区间是最短的
}
return ret == nums_size ? -1 : ret;
}
};
3. 1589. 所有排列中的最大和
思路:
利用差分数组统计同一位置的查询次数,将将更大的数放在访问次数更多的位置,获取最大值。实现细节见代码及注释。
class Solution {
#define mod 1000000007
public:
int maxSumRangeQuery(vector<int>& nums, vector<vector<int>>& requests) {
int requests_size = requests.size(), i;
int nums_size = nums.size();
int appear_times[nums_size + 1];
int sum = 0;
long long ans = 0;
vector<int> cnt;
memset(appear_times, 0, sizeof(appear_times));
for (i = 0; i < requests_size; ++i) { /* 利用差分数组记录查询区间 */
int l = requests[i][0];
int r = requests[i][1];
appear_times[l]++;
appear_times[r + 1]--;
}
for (i = 0; i < nums_size; ++i) { /* 统计每个某个位置被访问的次数 */
sum += appear_times[i];
cnt.push_back(sum);
}
sort(cnt.begin(), cnt.end()); // 按照访问次数升序排列
sort(nums.begin(), nums.end());
for (i = 0; i < nums_size; ++i) { /* 将更大的数放在访问次数更多的位置,获取最大值 */
ans += ((long long)nums[i] * (long long)cnt[i]) % mod; // 注意强转,不然爆int
ans %= mod;
}
return ans;
}
};
4. 1712. 将数组分成三个子数组的方案数
思路:
class Solution {
#define mod 1000000007
#define ll long long
public:
int waysToSplit(vector<int>& nums) {
int nums_size = nums.size(), i, ans = 0;
ll sum[nums_size + 1];
sum[0] = 0;
for (i = 1; i <= nums_size; ++i) sum[i] = sum[i - 1] + nums[i - 1];
// | left | mid | right |
for (int i = 1, l = 0, r = 0; i <= nums_size - 2; ++i) { /* 动态尝试中间区域[l, r],枚举所有可能的情况 */
l = max(i + 1, l);
while (l < nums_size && sum[l] - sum[i] < sum[i]) ++l;
r = max(l, r);
while (r < nums_size && sum[r] - sum[i] <= sum[nums_size] - sum[r]) ++r;
ans = (ans + r - l) % mod;
}
return ans;
}
};
东方欲晓,莫道君行早。

浙公网安备 33010602011771号