📅  最后修改于: 2023-12-03 14:58:17.277000             🧑  作者: Mango
给定一个长度为 N 的序列,求长度最多为 K 的包含不同素数元素的子序列的计数。
我们设 $f(i, j, S)$ 表示考虑到第 $i$ 个位置,已经选择了 $j$ 个素数,当前选择的素数集合为 $S$ 的方案数。
对于一个状态 $f(i, j, S)$,可以考虑当前位置 $i$ 对答案的贡献。
如果 $a_i$ 是素数,那么 $f(i, j, S)$ 可以转移到 $f(i+1, j+1, S\cup{a_i})$。
如果 $a_i$ 不是素数,那么 $f(i, j, S)$ 可以转移到 $f(i+1, j, S)$。
我们需要特别考虑 $j$ 是否已经达到了上限 $K$ 的情况。
如果 $j=K$,那么 $f(i, K, S)$ 只能向 $f(i+1, K, S)$ 转移。
如果 $j<K$,那么 $f(i, j, S)$ 可以向 $f(i+1, j, S)$ 和 $f(i+1, j+1, S\cup{a_i})$ 转移。
为了避免重复计算,我们可以将 $S$ 表示成一个数,其中二进制第 $i$ 位表示 $i$ 是否在当前的素数集合中,这样就可以省去对集合的操作了。
状态数为 $O(NK2^P)$,转移一次需要 $O(P)$ 的时间,因此总时间复杂度为 $O(NK2^PP)$。其中 $P$ 表示素数的个数。
def count_subsequences(a, K):
INF = 0x3f3f3f3f
n = len(a)
primes = [p for p in range(2, 100) if all(p % d != 0 for d in range(2, int(p**0.5)+1))]
p2i = {p: i for i, p in enumerate(primes)}
f = [[[0]*2**len(primes) for _ in range(K+1)] for _ in range(n+1)]
f[0][0][0] = 1
for i in range(n):
for j in range(K+1):
for s in range(2**len(primes)):
if f[i][j][s] == 0:
continue
f[i+1][j][s] += f[i][j][s]
if j == K:
continue
if a[i] in primes:
s2 = s | (1 << p2i[a[i]])
f[i+1][j+1][s2] += f[i][j][s]
else:
f[i+1][j][s] += f[i][j][s]
ans = 0
for j in range(K+1):
for s in range(2**len(primes)):
if bin(s).count('1') == j:
ans += f[n][j][s]
return ans
引用本文请参考以下 Bibtex 格式的引用数据:
@misc{lingxiaoding:2021,
title = "长度最多为 K 的包含不同素数元素的子序列的计数",
author = "Ding, Lingxiao",
year = "2021",
month = "8",
day = "23",
url = "https://github.com/lingxiaoding/templates/blob/main/subsequence_counting_with_prime_factors.md"
}