📌  相关文章
📜  长度最多为 K 的包含不同素数元素的子序列的计数(1)

📅  最后修改于: 2023-12-03 14:58:17.277000             🧑  作者: Mango

长度最多为 K 的包含不同素数元素的子序列的计数

问题描述

给定一个长度为 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"
}