列表

详情


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 个无平方子集。

 

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: int squareFreeSubsets(vector<int>& nums) { } };

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 去掉空集

上一题