class Solution {
public:
int squareFreeSubsets(vector<int>& nums) {
}
};
2572. 无平方子集计数
给你一个正整数数组 nums
。
如果数组 nums
的子集中的元素乘积是一个 无平方因子数 ,则认为该子集是一个 无平方 子集。
无平方因子数 是无法被除 1
之外任何平方数整除的数字。
返回数组 nums
中 无平方 且 非空 的子集数目。因为答案可能很大,返回对 109 + 7
取余的结果。
nums
的 非空子集 是可以由删除 nums
中一些元素(可以不删除,但不能全部删除)得到的一个数组。如果构成两个子集时选择删除的下标不同,则认为这两个子集不同。
示例 1:
输入:nums = [3,4,4,5] 输出:3 解释:示例中有 3 个无平方子集: - 由第 0 个元素 [3] 组成的子集。其元素的乘积是 3 ,这是一个无平方因子数。 - 由第 3 个元素 [5] 组成的子集。其元素的乘积是 5 ,这是一个无平方因子数。 - 由第 0 个和第 3 个元素 [3,5] 组成的子集。其元素的乘积是 15 ,这是一个无平方因子数。 可以证明给定数组中不存在超过 3 个无平方子集。
示例 2:
输入:nums = [1] 输出:1 解释:示例中有 1 个无平方子集: - 由第 0 个元素 [1] 组成的子集。其元素的乘积是 1 ,这是一个无平方因子数。 可以证明给定数组中不存在超过 1 个无平方子集。
提示:
1 <= nums.length <= 1000
1 <= nums[i] <= 30
原站题解
python3 解法, 执行用时: 80 ms, 内存消耗: 15.3 MB, 提交时间: 2023-02-26 11:08:24
tmp = {1, 2, 3, 5, 6, 7, 10, 11, 13, 14, 15, 17, 19, 21, 22, 23, 26, 29, 30} mod = 10 ** 9 + 7 class Solution: def squareFreeSubsets(self, nums: List[int]) -> int: cnt = Counter(num for num in nums if num in tmp) note = Counter({1: 1}) for v in cnt: if v != 1: new_note = Counter() for v1 in note: new_note[v1] += note[v1] if gcd(v, v1) == 1: new_note[v1 * v] += note[v1] * cnt[v] new_note[v1] %= mod note = new_note return (sum(note.values()) * pow(2, cnt[1], mod) - 1) % mod
golang 解法, 执行用时: 12 ms, 内存消耗: 2.4 MB, 提交时间: 2023-02-26 11:07:54
var primes = [...]int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} var nsq2mask = [31]int{} // nsq2mask[i] 为 i 对应的质数集合(用二进制表示) func init() { for i := 2; i <= 30; i++ { for j, p := range primes { if i%p == 0 { if i%(p*p) == 0 { // 有平方因子 nsq2mask[i] = -1 break } nsq2mask[i] |= 1 << j // 把 j 加到集合中 } } } } func squareFreeSubsets(nums []int) int { const mod int = 1e9 + 7 const m = 1 << len(primes) f := [m]int{1} // f[j] 表示恰好组成集合 j 的方案数,其中空集的方案数为 1 for _, x := range nums { if mask := nsq2mask[x]; mask >= 0 { // x 是 NSQ for j := m - 1; j >= mask; j-- { if j|mask == j { // mask 是 j 的子集 f[j] = (f[j] + f[j^mask]) % mod // 不选 mask + 选 mask } } } } ans := 0 for _, v := range f { ans += v } return (ans - 1) % mod // -1 去掉空集 }
cpp 解法, 执行用时: 20 ms, 内存消耗: 8 MB, 提交时间: 2023-02-26 11:07:32
class Solution { static constexpr int PRIMES[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; static constexpr int MOD = 1e9 + 7, MX = 30, N_PRIMES = 10, M = 1 << N_PRIMES; public: int squareFreeSubsets(vector<int> &nums) { int nsq2mask[MX + 1]{}; // nsq2mask[i] 为 i 对应的质数集合(用二进制表示) for (int i = 2; i <= MX; ++i) for (int j = 0; j < N_PRIMES; ++j) { int p = PRIMES[j]; if (i % p == 0) { if (i % (p * p) == 0) { // 有平方因子 nsq2mask[i] = -1; break; } nsq2mask[i] |= 1 << j; // 把 j 加到集合中 } } int f[M]{1}; // f[j] 表示恰好组成集合 j 的方案数,其中空集的方案数为 1 for (int x : nums) if (int mask = nsq2mask[x]; mask >= 0) // x 是 NSQ for (int j = M - 1; j >= mask; --j) if ((j | mask) == j) // mask 是 j 的子集 f[j] = (f[j] + f[j ^ mask]) % MOD; // 不选 mask + 选 mask return (accumulate(f, f + M, 0L) - 1) % MOD; // -1 去掉空集 } };
java 解法, 执行用时: 11 ms, 内存消耗: 40.6 MB, 提交时间: 2023-02-26 11:07:12
class Solution { private static final int[] PRIMES = new int[]{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; private static final int MOD = (int) 1e9 + 7, MX = 30, N_PRIMES = PRIMES.length, M = 1 << N_PRIMES; private static final int[] NSQ_TO_MASK = new int[MX + 1]; // NSQ_TO_MASK[i] 为 i 对应的质数集合(用二进制表示) static { for (int i = 2; i <= MX; ++i) for (int j = 0; j < N_PRIMES; ++j) { int p = PRIMES[j]; if (i % p == 0) { if (i % (p * p) == 0) { // 有平方因子 NSQ_TO_MASK[i] = -1; break; } NSQ_TO_MASK[i] |= 1 << j; // 把 j 加到集合中 } } } public int squareFreeSubsets(int[] nums) { var f = new int[M]; // f[j] 表示恰好组成集合 j 的方案数 f[0] = 1; // 空集的方案数为 1 for (int x : nums) { int mask = NSQ_TO_MASK[x]; if (mask >= 0) // x 是 NSQ for (int j = M - 1; j >= mask; --j) if ((j | mask) == j) // mask 是 j 的子集 f[j] = (f[j] + f[j ^ mask]) % MOD; // 不选 mask + 选 mask } var ans = 0L; for (int v : f) ans += v; return (int) ((ans - 1) % MOD); // -1 去掉空集 } }
python3 解法, 执行用时: 700 ms, 内存消耗: 15 MB, 提交时间: 2023-02-26 11:06:55
''' 把无平方因子数的数字记作NSQ。 对于每个[2,30]内的NSQ,通过预处理得到对应的质数集合,用二进制表示。 二进制从低到高第i个比特为1表示第i个质数在集合中,为0表示第i个质数不在集合中。 那么把每个是NSQ的nums[i]转换成对应的质数集合,题自就变成「选一些不相交的质数集合,它们的并集恰好为集合,的方案数」。 ''' PRIMES = 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 NSQ_TO_MASK = [0] * 31 # NSQ_TO_MASK[i] 为 i 对应的质数集合(用二进制表示) for i in range(2, 31): for j, p in enumerate(PRIMES): if i % p == 0: if i % (p * p) == 0: # 有平方因子 NSQ_TO_MASK[i] = -1 break NSQ_TO_MASK[i] |= 1 << j # 把 j 加到集合中 class Solution: def squareFreeSubsets(self, nums: List[int]) -> int: MOD = 10 ** 9 + 7 M = 1 << len(PRIMES) f = [0] * M # f[j] 表示恰好组成集合 j 的方案数 f[0] = 1 # 空集的方案数为 1 for x in nums: mask = NSQ_TO_MASK[x] if mask >= 0: # x 是 NSQ for j in range(M - 1, mask - 1, -1): if (j | mask) == j: # mask 是 j 的子集 f[j] = (f[j] + f[j ^ mask]) % MOD # 不选 mask + 选 mask return (sum(f) - 1) % MOD # -1 去掉空集