chenfy27的刷题记录

导航

leetcode2902 和带限制的子多重集合的数目

给定数组nums[n]和整数l,r,nums中的元素可能会重复,要求从nums中选择若干个元素,其元素和在[l,r]内,有多少种不同方案,结果对1E9+7取模。注:空集合的结果为0,相等的元素之间没有区别。
1<=n<=2E4; 0<=nums[i]<=2E4; sum(nums[i])<=2E4; 0<=l<=r<=2E4

分析:
1、存在相等元素,且没有区别,可以看成是一个多重背包问题。
2、由于sum(nums[i])<=2E4,可以估算出不同元素的个数(记为m)不超过200,因为1+2+..+200>2E4。
3、如果用二进制优化,时间复杂度会超,因此要用类似单调队列优化dp的方式优化掉时间复杂度中的log,大致思路如下:
(1)状态转移方程:dp[i][j] = dp[i-1][j] + dp[i-1][j-v] + dp[i-1][j-2v] + dp[i-1][j-3v] + ...,可以看到第二维模v同余,即计算dp[i][j]时,只依赖dp[i-1]只与j同余的那些项。
(2)外层按余数0~v-1从小到大的顺序,内层按a, a+v, a+2v的顺序,计算dp。对于某个固定的余数,由于j较大时,未必有足够的相同元素,因此用滑动窗口来维护。
4、元素0需要单独处理。

int dq[20005];
class Solution {
public:
    int countSubMultisets(vector<int>& nums, int l, int r) {
        int n = nums.size();
        std::sort(nums.begin(), nums.end());
        std::vector<std::pair<int,int>> vp;
        int cnt0 = 0;
        for (int i = 0, j = 0; i < n; i = j) {
            for (j = i; j < n && nums[j] == nums[i]; j++);
            if (nums[i] == 0) {
                cnt0 = j - i;
            } else {
                vp.emplace_back(nums[i], j - i);
            }
        }
        int m = vp.size();
        std::vector<mint> dp1(r + 1), dp2(r + 1);

        for (int i = 0; i <= m; i++) {
            dp1[0] = 1;
        }
        for (int i = 1; i <= m; i++) {
            int val = vp[i-1].first;
            int cnt = vp[i-1].second;
            for (int j = 0; j < val; j++) {
                mint sum = 0;
                int L = 0, R = 0;
                for (int k = j; k <= r; k += val) {
                    sum += dp1[k];
                    dq[R++] = k;
                    if (dq[R-1] - dq[L] > val * cnt) {
                        sum -= dp1[dq[L++]];
                    }
                    dp2[k] = sum;
                }
            }
            std::swap(dp1, dp2);
        }
        mint ans = 0;
        for (int j = l; j <= r; j++) {
            ans += dp1[j];
        }
        if (cnt0) ans *= cnt0 + 1;
        return ans.val();
    }
};

posted on 2025-01-12 19:48  chenfy27  阅读(8)  评论(0)    收藏  举报