📌  相关文章
📜  其乘积为复合数的所有子序列的计数(1)

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

计算复合数乘积子序列计数

在这个问题中,给出一个数字序列,需要计算所有子序列的乘积为复合数的计数。

复合数是指有两个或多个质因数的正整数。

解题思路

一个数字序列的子序列包含从原序列中挑选出的一些元素,但不必按照原序列的顺序排列。

我们可以使用动态规划的方法解决这个问题。

假设 $dp[i]$ 表示以第 $i$ 个数字为结尾的子序列中,乘积为复合数的计数。

每当加入一个数字时,我们首先要判断这个数字是不是质数,如果是,则 $dp[i]=0$,因为一个数字本身不能算作是复合数。

如果不是质数,则需要判断该数字能否与前面的数字(即当前数字的前面所有的数字)组成复合数。

设 $mul[i]$ 表示数字序列中前 $i$ 个数字的乘积。

由于乘积会越来越大,而我们要对每一个数判断是否可以组合成复合数,因此我们需要对乘积开一个哈希表,哈希表中存储每个数字的质因子个数。

在计算 $mul[i]$ 的同时,我们可以使用之前已经辨别出来的质数来更新哈希表中的各个质因子的数量。

当加入第 $i$ 个数字时,我们需要判断前面所有的数字能否与该数字组成复合数。

具体地,我们可以枚举前面的数字 $j$,对于 $j$ 在哈希表中对应的质因子,将其对应的质因子个数减去 $1$,然后再取 $mul[i]$ 与 $j$ 的乘积,判断这个乘积的因子个数是否大于 $1$,如果是,则说明这个乘积是一个复合数,将 $dp[i]$ 加上 $dp[j]+1$。

最后将第 $i$ 个数字加入哈希表中,将其对应的质因子计数加 $1$,更新 $mul[i]$,然后将 $dp[i]$ 加上 $dp[i-1]$,表示以该数字为结尾的子序列中,除了以该数字为结尾的子序列外,另有 $dp[i-1]$ 个子序列的乘积是复合数。

最终的答案即为 $dp$ 数组中所有元素的和。

代码实现
import collections
import math

def count_composite_product_subsequence(nums):
    # 计算每个数字的质因数分解
    # factor_counts[i] 表示数字 i 的质因数以及对应的因子个数
    factor_counts = [collections.Counter() for _ in range(len(nums))]

    for i in range(len(nums)):
        if nums[i] < 2:
            continue

        # 如果 nums[i] 是一个质数,将其加入哈希表中
        if factor_counts[i][nums[i]] == 0:
            factor_counts[i][nums[i]] += 1

        # 计算乘积的质因数
        # 遍历 nums[:i] 中所有已经加入哈希表中的数字,更新乘积的质因数个数
        for p in factor_counts[i-1]:
            factor_counts[i].update(factor_counts[i-1])

            cnt = factor_counts[i][p] + 1
            while nums[i] % p == 0:
                nums[i] //= p
                factor_counts[i][p] += 1

            cnt -= factor_counts[i][p]
            if cnt > 0:
                factor_counts[i][p] += cnt

        # 如果 nums[i] 仍然有除数,则将其加入哈希表中
        if nums[i] > 1 and factor_counts[i][nums[i]] == 0:
            factor_counts[i][nums[i]] += 1

    # 计算所有子序列中,乘积为复合数的计数
    # dp[i] 表示以 nums[i] 为结尾的子序列中,乘积为复合数的计数
    dp = [0] * len(nums)

    for i in range(len(nums)):
        if len(factor_counts[i]) == 0:
            continue

        if len(factor_counts[i]) == 1 and 2 not in factor_counts[i]:
            continue

        for j in range(i):
            # 判断 nums[i] 和 nums[j] 是否可以组成复合数
            factor_count = collections.Counter(factor_counts[i])
            for p in factor_counts[j]:
                factor_count.update(factor_counts[j])

                cnt = factor_count[p] - factor_counts[j][p]
                while nums[i] % p == 0:
                    nums[i] //= p
                    factor_count[p] += 1

                cnt -= factor_count[p] - factor_counts[i][p]
                if cnt > 0:
                    factor_count[p] += cnt

            # 如果乘积为复合数,则更新 dp[i]
            mul = nums[i] * nums[j]
            if len(factor_count) > 1 or (len(factor_count) == 1 and 2 in factor_count):
                dp[i] += dp[j] + 1

        # 更新 dp[i]
        dp[i] += dp[i-1]

    return dp[-1]
复杂度分析

计算每个数字的质因数,时间复杂度为 $O(n\sqrt{n})$;

针对每个数字,计算其乘积的质因数,时间复杂度为 $O(n\sqrt{n})$;

计算 $dp$ 数组的过程中,需要对每个数字,枚举其前面的所有数字,判断是否能够组成复合数,时间复杂度为 $O(n^2\sqrt{n})$。

因此,总时间复杂度为 $O(n^2\sqrt{n})$,空间复杂度为 $O(n\sqrt{n})$。