六月集训(第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;
    }
};
posted @ 2022-06-08 22:55  番茄元  阅读(32)  评论(0)    收藏  举报