LeetCode 5849. 好子集的数目(状压dp)

Posted live4m

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 5849. 好子集的数目(状压dp)相关的知识,希望对你有一定的参考价值。

题意:

给你一个整数数组 nums 。如果 nums 的一个子集中,所有元素的乘积可以用若干个 互不相同的质数 相乘得到,
那么我们称它为 好子集 。

比方说,如果 nums = [1, 2, 3, 4][2, 3][1, 2, 3][1, 3] 是 好 子集,乘积分别为 6 = 2*36 = 2*33 = 3[1, 4][4] 不是 好 子集,因为乘积分别为 4 = 2*24 = 2*2 。
请你返回 nums 中不同的 好 子集的数目对 109 + 7 取余 的结果。

nums 中的 子集 是通过删除 nums 中一些(可能一个都不删除,也可能全部都删除)元素后剩余元素组成的数组。
如果两个子集删除的下标不同,那么它们被视为不同的子集。

数据范围:
1 <= nums.length <= 1e5
1 <= nums[i] <= 30

解法:

[1,30]的质因子只有10,那么质因子使用状态数只有(1<<10)=1e3.

令d[s]表示前i个数,使用质因子状态为s的方案数,dp复杂度O(n*1e3),
然而n是1e5,需要优化:
由于状态只有1e3,因子可以将n个数压缩为1e3个数,那么dp复杂度变为O(1e3*1e3).

code:

const int mod=1e9+7;
const int maxm=1e5+5;
int p[]={2,3,5,7,11,13,17,19,23,29};
int pos[maxm];
int cnt[maxm];
int p2[maxm];
int d[maxm];
class Solution {
public:
    int numberOfGoodSubsets(vector<int>& nums){
        //init
        for(int i=p2[0]=1;i<maxm;i++)p2[i]=p2[i-1]*2%mod;
        for(int i=0;i<(1<<10);i++)cnt[i]=0;
        for(int i=0;i<(1<<10);i++)d[i]=0;
        //
        for(int i=0;i<10;i++)pos[p[i]]=i;
        int one=0;
        for(auto x:nums){
            if(x==1){
                one++;continue;
            }
            int ok=1;
            int t=0;
            for(int j=0;j<10;j++){
                if(x%p[j]==0){
                    int c=0;
                    while(x%p[j]==0)x/=p[j],c++;
                    if(c!=1){
                        ok=0;
                        break;
                    }else{
                        t|=(1<<pos[p[j]]);
                    }
                }
            }
            if(ok)cnt[t]++;
        }
        d[0]=1;
        for(int i=1;i<(1<<10);i++){
            if(!cnt[i])continue;
            for(int j=(1<<10)-1;j>=0;j--){
                if(!(j&i)){
                    d[j|i]=(d[j|i]+1ll*d[j]*cnt[i]%mod)%mod;
                }
            }
        }
        int ans=0;
        for(int i=1;i<(1<<10);i++){
            ans=(ans+d[i])%mod;
        }
        ans=1ll*ans*p2[one]%mod;
        return ans;
    }
};

以上是关于LeetCode 5849. 好子集的数目(状压dp)的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 1986. 完成任务的最少工作时间段(状压dp,子集枚举)

算法leetcode每日一练2044. 统计按位或能得到最大值的子集数目

leetcode中等2044. 统计按位或能得到最大值的子集数目

LeetCode 2044. 统计按位或能得到最大值的子集数目

JZOJ 5849 d

LeetCode 393. UTF-8 编码验证 / 599. 两个列表的最小索引总和 / 2044. 统计按位或能得到最大值的子集数目