chenfy27的刷题记录

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

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   chenfy27  阅读(6)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示