LC 2572. 无平方子集计数
https://leetcode.cn/problems/count-the-number-of-square-free-subsets/
这题很容易看出来是状态dp, 但是递推公式似乎不是那么直接。我最最开始的想法是
- 如果当前数值是x,遍历st, 枚举每个bit上的内容
- 如果 `(x & (1 << bit))` 并且 `(st & (1 << bit))`, 那么不能使用
这种思路除了时间复杂度更高之外,还有就是对于1不太好处理,因为1可以放在所有的集合里面,1需要单独做处理。
这样写来写出的代码就特别地长,比如下面这样
class Solution:
def squareFreeSubsets(self, nums: List[int]) -> int:
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
dp = [0] * (1 << len(primes))
dp[0] = 1
def hasSquareFactor(x):
for p in primes:
if x % p == 0:
c = 0
while x % p == 0:
x = x // p
c += 1
if c >= 2: return True
return False
tmp = []
ONE = 0
for x in nums:
if x == 1:
ONE += 1
elif hasSquareFactor(x):
continue
else:
tmp.append(x)
nums = tmp
MOD = 10 ** 9 + 7
for x in nums:
bits = []
st = 0
for idx, p in enumerate(primes):
if x % p == 0:
x = x // p
bits.append(idx)
st = st | (1 << idx)
for j in reversed(range(len(dp))):
match = True
for b in bits:
if j & (1 << b):
match = False
break
if match:
dp[j | st] = (dp[j | st] + dp[j]) % MOD
def pow(a, b):
s = 1
while b:
if b & 0x1:
s = s * a
s = s % MOD
b = b // 2
a = (a * a) % MOD
return s
S = pow(2, ONE)
ans = sum(dp[1:])
ans = (ans + 1) * S - 1
ans = ans % MOD
return ans
题解中的解法就比较简单了,递推思路就不太一样,此外还可以做预处理。
- 对于x的状态假设是m, 遍历所有的st状态
- 如果 `(st | m) == st`, 那么说明全部包含bit, 那么两者只能选1个
- 选择状态st, 但是不选择m
- 选择状态st ^ m, 同时选择m.
- 可以看到其实这里就能把1选入进去了,因为 `mask[1] = 0`.
class Solution:
def squareFreeSubsets(self, nums: List[int]) -> int:
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
M = 1 << len(primes)
mask = [0] * 31
# preprocess.
for x in range(1, 31):
for idx, p in enumerate(primes):
if x % p == 0:
if (x // p) % p == 0:
mask[x] = -1
else:
mask[x] |= (1 << idx)
MOD = 10 ** 9 + 7
dp = [0] * M
dp[0] = 1
for x in nums:
m = mask[x]
if m >= 0: # mask[1] = 0
for st in reversed(range(M)):
if (st | m) == st:
# 选择st, 不选择m. 或者是选择st ^ m, 选择m.
dp[st] = (dp[st] + dp[st ^ m]) % MOD
return (sum(dp) - 1) % MOD