class Solution {
public:
int countSubMultisets(vector<int>& nums, int l, int r) {
}
};
100029. 和带限制的子多重集合的数目
给你一个下标从 0 开始的非负整数数组 nums
和两个整数 l
和 r
。
请你返回 nums
中子多重集合的和在闭区间 [l, r]
之间的 子多重集合的数目 。
由于答案可能很大,请你将答案对 109 + 7
取余后返回。
子多重集合 指的是从数组中选出一些元素构成的 无序 集合,每个元素 x
出现的次数可以是 0, 1, ..., occ[x]
次,其中 occ[x]
是元素 x
在数组中的出现次数。
注意:
0
。
示例 1:
输入:nums = [1,2,2,3], l = 6, r = 6 输出:1 解释:唯一和为 6 的子集合是 {1, 2, 3} 。
示例 2:
输入:nums = [2,1,4,2,7], l = 1, r = 5 输出:7 解释:和在闭区间 [1, 5] 之间的子多重集合为 {1} ,{2} ,{4} ,{2, 2} ,{1, 2} ,{1, 4} 和 {1, 2, 2} 。
示例 3:
输入:nums = [1,2,1,3,5,2], l = 3, r = 5 输出:9 解释:和在闭区间 [3, 5] 之间的子多重集合为 {3} ,{5} ,{1, 2} ,{1, 3} ,{2, 2} ,{2, 3} ,{1, 1, 2} ,{1, 1, 3} 和 {1, 2, 2} 。
提示:
1 <= nums.length <= 2 * 104
0 <= nums[i] <= 2 * 104
nums
的和不超过 2 * 104
。0 <= l <= r <= 2 * 104
原站题解
golang 解法, 执行用时: 40 ms, 内存消耗: 7.4 MB, 提交时间: 2023-10-23 00:12:59
func countSubMultisets(nums []int, l, r int) (ans int) { const mod = 1_000_000_007 total := 0 cnt := map[int]int{} for _, x := range nums { total += x cnt[x]++ } if l > total { return } r = min(r, total) f := make([]int, r+1) f[0] = cnt[0] + 1 delete(cnt, 0) sum := 0 for x, c := range cnt { newF := append([]int{}, f...) sum = min(sum+x*c, r) // 到目前为止,能选的元素和至多为 sum for j := x; j <= sum; j++ { // 把循环上界从 r 改成 sum 可以快不少 newF[j] += newF[j-x] if j >= (c+1)*x { newF[j] -= f[j-(c+1)*x] // 注意这里有减法,可能产生负数 } newF[j] %= mod } f = newF } for _, v := range f[l:] { ans += v } return (ans%mod + mod) % mod // 调整成非负数 } func countSubMultisets2(nums []int, l, r int) (ans int) { const mod = 1_000_000_007 total := 0 cnt := map[int]int{} for _, x := range nums { total += x cnt[x]++ } if l > total { return } r = min(r, total) f := make([]int, r+1) f[0] = cnt[0] + 1 delete(cnt, 0) sum := 0 for x, c := range cnt { sum = min(sum+x*c, r) for j := x; j <= sum; j++ { f[j] = (f[j] + f[j-x]) % mod // 原地计算同余前缀和 } for j := sum; j >= x*(c+1); j-- { f[j] = (f[j] - f[j-x*(c+1)]) % mod // 两个同余前缀和的差 } } for _, v := range f[l:] { ans += v } return (ans%mod + mod) % mod // 调整成非负数 } func min(a, b int) int { if b < a { return b }; return a }
java 解法, 执行用时: 26 ms, 内存消耗: 44.8 MB, 提交时间: 2023-10-23 00:12:03
class Solution { public int countSubMultisets(List<Integer> nums, int l, int r) { final int MOD = 1_000_000_007; int total = 0; var cnt = new HashMap<Integer, Integer>(); for (int x : nums) { total += x; cnt.merge(x, 1, Integer::sum); } if (l > total) { return 0; } r = Math.min(r, total); int[] f = new int[r + 1]; f[0] = cnt.getOrDefault(0, 0) + 1; cnt.remove(0); int sum = 0; for (var e : cnt.entrySet()) { int x = e.getKey(), c = e.getValue(); sum = Math.min(sum + x * c, r); for (int j = x; j <= sum; j++) { f[j] = (f[j] + f[j - x]) % MOD; // 原地计算同余前缀和 } for (int j = sum; j >= x * (c + 1); j--) { f[j] = (f[j] - f[j - x * (c + 1)] + MOD) % MOD; // 两个同余前缀和的差 } } int ans = 0; for (int i = l; i <= r; ++i) { ans = (ans + f[i]) % MOD; } return ans; } public int countSubMultisets2(List<Integer> nums, int l, int r) { final int MOD = 1_000_000_007; int total = 0; var cnt = new HashMap<Integer, Integer>(); for (int x : nums) { total += x; cnt.merge(x, 1, Integer::sum); } if (l > total) { return 0; } r = Math.min(r, total); int[] f = new int[r + 1]; f[0] = cnt.getOrDefault(0, 0) + 1; cnt.remove(0); int sum = 0; for (var e : cnt.entrySet()) { int x = e.getKey(), c = e.getValue(); int[] newF = f.clone(); sum = Math.min(sum + x * c, r); // 到目前为止,能选的元素和至多为 sum for (int j = x; j <= sum; j++) { // 把循环上界从 r 改成 sum 可以快不少 newF[j] = (newF[j] + newF[j - x]) % MOD; if (j >= (c + 1) * x) { newF[j] = (newF[j] - f[j - (c + 1) * x] + MOD) % MOD; // 避免减法产生负数 } } f = newF; } int ans = 0; for (int i = l; i <= r; ++i) { ans = (ans + f[i]) % MOD; } return ans; } }
cpp 解法, 执行用时: 120 ms, 内存消耗: 82.8 MB, 提交时间: 2023-10-23 00:11:32
class Solution { public: int countSubMultisets(vector<int> &nums, int l, int r) { const int MOD = 1e9 + 7; int total = 0; unordered_map<int, int> cnt; for (int x: nums) { total += x; cnt[x]++; } if (l > total) { return 0; } r = min(r, total); vector<int> f(r + 1); f[0] = cnt[0] + 1; cnt.erase(0); int sum = 0; for (auto [x, c]: cnt) { auto new_f = f; sum = min(sum + x * c, r); // 到目前为止,能选的元素和至多为 sum for (int j = x; j <= sum; j++) { // 把循环上界从 r 改成 sum 可以快不少 new_f[j] = (new_f[j] + new_f[j - x]) % MOD; if (j >= (c + 1) * x) { new_f[j] = (new_f[j] - f[j - (c + 1) * x] + MOD) % MOD; // 避免减法产生负数 } } f = move(new_f); } int ans = 0; for (int i = l; i <= r; i++) { ans = (ans + f[i]) % MOD; } return ans; } int countSubMultisets2(vector<int> &nums, int l, int r) { const int MOD = 1e9 + 7; int total = 0; unordered_map<int, int> cnt; for (int x: nums) { total += x; cnt[x]++; } if (l > total) { return 0; } r = min(r, total); vector<int> f(r + 1); f[0] = cnt[0] + 1; cnt.erase(0); int sum = 0; for (auto [x, c]: cnt) { sum = min(sum + x * c, r); for (int j = x; j <= sum; j++) { f[j] = (f[j] + f[j - x]) % MOD; // 原地计算同余前缀和 } for (int j = sum; j >= x * (c + 1); j--) { f[j] = (f[j] - f[j - x * (c + 1)] + MOD) % MOD; // 两个同余前缀和的差 } } int ans = 0; for (int i = l; i <= r; i++) { ans = (ans + f[i]) % MOD; } return ans; } };
python3 解法, 执行用时: 1320 ms, 内存消耗: 18.3 MB, 提交时间: 2023-10-23 00:10:59
class Solution: def countSubMultisets(self, nums: List[int], l: int, r: int) -> int: MOD = 10 ** 9 + 7 total = sum(nums) if l > total: return 0 r = min(r, total) cnt = Counter(nums) f = [cnt[0] + 1] + [0] * r del cnt[0] s = 0 for x, c in cnt.items(): new_f = f.copy() s = min(s + x * c, r) # 到目前为止,能选的元素和至多为 s for j in range(x, s + 1): # 把循环上界从 r 改成 s,能快一倍 new_f[j] += new_f[j - x] if j >= (c + 1) * x: new_f[j] -= f[j - (c + 1) * x] new_f[j] %= MOD f = new_f return sum(f[l:]) % MOD class Solution2: def countSubMultisets(self, nums: List[int], l: int, r: int) -> int: MOD = 10 ** 9 + 7 total = sum(nums) if l > total: return 0 r = min(r, total) cnt = Counter(nums) f = [cnt[0] + 1] + [0] * r del cnt[0] s = 0 for x, c in cnt.items(): s = min(s + x * c, r) for j in range(x, s + 1): f[j] = (f[j] + f[j - x]) % MOD # 原地计算同余前缀和 t = (c + 1) * x for j in range(s, t - 1, -1): f[j] = (f[j] - f[j - t]) % MOD # 两个同余前缀和的差 return sum(f[l:]) % MOD