Count the Number of Square-Free Subsets

Count the Number of Square-Free Subsets

You are given a positive integer 0-indexed array nums .

A subset of the array nums is square-free if the product of its elements is a square-free integer.

A square-free integer is an integer that is divisible by no square number other than 1.

Return the number of square-free non-empty subsets of the array nums. Since the answer may be too large, return it modulo 109+7.

A non-empty subset of nums is an array that can be obtained by deleting some (possibly none but not all) elements from nums . Two subsets are different if and only if the chosen indices to delete are different.

Example 1:

Input: nums = [3,4,4,5]
Output: 3
Explanation: There are 3 square-free subsets in this example:
- The subset consisting of the 0th element [3]. The product of its elements is 3, which is a square-free integer.
- The subset consisting of the 3rd element [5]. The product of its elements is 5, which is a square-free integer.
- The subset consisting of 0th and 3rd elements [3,5]. The product of its elements is 15, which is a square-free integer.
It can be proven that there are no more than 3 square-free subsets in the given array.

Example 2:

Input: nums = [1]
Output: 1
Explanation: There is 1 square-free subset in this example:
- The subset consisting of the 0th element [1]. The product of its elements is 1, which is a square-free integer.
It can be proven that there is no more than 1 square-free subset in the given array.

Constraints:

  • 1nums.length1000
  • 1nums[i]30

 

解题思路

  首先一个数是所谓的“无平方因子数”等价于该数的质因数分解中每一个质因子的次数恰好是1。因此如果一个数的某个质因子的出现次数超过1,那么该数一定不是“无平方因子数”,与该数相乘得到的数明显也不是。

  因此我们先把所有质因子次数恰好为1的数先筛出来,得到数组a,然后再从a中选择若干个数进行乘积,使得乘积结果的每个质因子的次数也要恰好为1,那么这就是一个合法的子集。注意到相乘的结果本质就是选择若干个均不同的质因子进行乘积。

  由于每个数最大不超过30,而30以内的质数只有10个,因此可以考虑状压dp,状态一共有10个二进制位,如果状态的某位是1意味着相乘的结果包含该位所对应的质数。

  定义状态f(i,j)表示从前i个数中选,乘积结果的状态为j(即包含了哪些质数)的所有方案的个数。根据第i个数是否选择来进行状态划分。设stai表示第i个数ai所包含的质因子的状态,状态转移方程为f(i,j)=f(i1,j)+f(i1,jstai)    (其中要保证j含有stai中的所有质因子,即保证j & stai=stai)

  其中如果ai=1,那么1有选或不选这两种状态,因此f(i,j)=f(i1,j)+f(i1,j)。而如果定义st1=0,那么上面那条状态转移方程同样适用,因为j & st1=j,因此f(i1,jstai)=f(i1,j)

  AC代码如下,时间复杂度为O(n210)

复制代码
 1 class Solution {
 2 public:
 3     int mod = 1e9 + 7;
 4     
 5     int squareFreeSubsets(vector<int>& nums) {
 6         int primes[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};  // 30以内的质数
 7         vector<int> a, st(31);
 8         for (auto &x : nums) {  // 对每个数进行质因数分解
 9             bool flag = true;   // 该数的每个质因子的次数是否均为1
10             int t = x;
11             for (int i = 0; i < 10; i++) {
12                 int p = primes[i];
13                 if (t % p == 0) {
14                     int cnt = 0;
15                     st[x] |= 1 << i;
16                     while (t % p == 0) {
17                         cnt++;
18                         t /= p;
19                     }
20                     if (cnt > 1) {  // 质因子p的次数超过1个
21                         flag = false;
22                         break;
23                     }
24                 }
25             }
26             if (flag) a.push_back(x);   // 筛出合法的数
27         }
28         int n = a.size();
29         vector<vector<int>> f(n + 1, vector<int>(1 << 10));
30         f[0][0] = 1;
31         for (int i = 1; i <= n; i++) {
32             for (int j = 0; j < 1 << 10; j++) {
33                 f[i][j] = f[i - 1][j];
34                 int t = st[a[i - 1]];
35                 if ((j & t) == t) f[i][j] = (f[i][j] + f[i - 1][j ^ t]) % mod;  // 如果要选择第i个数,那么j所表示的状态要包含第i个数的状态
36             }
37         }
38         int ret = 0;
39         for (int i = 0; i < 1 << 10; i++) { // 状态0表示空集和选择1的方案数
40             ret = (ret + f[n][i]) % mod;
41         }
42         return (ret - 1 + mod) % mod;   // -1是除去空集的情况
43     }
44 };
复制代码

 

参考资料

  状压 DP:https://leetcode.cn/problems/count-the-number-of-square-free-subsets/solution/zhuang-ya-dp-by-tsreaper-dwts/

posted @   onlyblues  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-02-19 波动数列
Web Analytics
点击右上角即可分享
微信分享提示