Given an array arr[] of size n. The task is to find a number of subsets whose product can be represented as a product of one or more distinct prime numbers. Modify your answer to the modulo of 109 + 7.
Constraints:
- 1 <= arr.size() <= 105
- 1< = arr[i] <= 30
Example:
Input: arr[] = [1, 2, 3, 4]
Output: 6
Explanation: The good subsets are:
- [1, 2]: product is 2, which is the product of distinct prime 2.
- [1, 2, 3]: product is 6, which is the product of distinct primes 2 and 3.
- [1, 3]: product is 3, which is the product of distinct prime 3.
- [2]: product is 2, which is the product of distinct prime 2.
- [2, 3]: product is 6, which is the product of distinct primes 2 and 3.
- [3]: product is 3, which is the product of distinct prime 3.
Input: arr[] = [2, 2, 3]
Output: 5
Explanation: The good subsets are : [2], [2], [2, 3], [2, 3], [3]
[Backtracking Approach] Using Recursion – O(2ⁿ × √m) Time and O(n) Space
The idea is to generate all possible subsets using recursion and backtracking. A subset is considered good only if no prime factor repeats and every selected number is square-free. For each element, we either include it in the subset or skip it, while tracking already used prime factors.
- Recursively try both choices for every element: take or skip
- Before taking a number, check whether it is square-free
- Extract distinct prime factors of the current number
- If any prime factor is already used, skip that number
- Otherwise, mark primes as used, continue recursion, and backtrack later
- Count only non-empty valid subsets
#include <bits/stdc++.h>
using namespace std;
// Check whether number is square free or not
// Example:
// 6 -> valid
// 12 -> invalid because 2 repeats
bool validNumber(int num)
{
for (int i = 2; i * i <= num; i++) {
int cnt = 0;
while (num % i == 0) {
cnt++;
num /= i;
// repeated prime factor found
if (cnt > 1)
return false;
}
}
return true;
}
// Store distinct prime factors
vector<int> getPrimeFactors(int num)
{
vector<int> primes;
for (int i = 2; i * i <= num; i++) {
if (num % i == 0) {
primes.push_back(i);
while (num % i == 0)
num /= i;
}
}
// remaining prime number
if (num > 1)
primes.push_back(num);
return primes;
}
int solve(int idx,
vector<int>& nums,
map<int,int>& usedPrime,
bool taken)
{
// all elements processed
if (idx == nums.size()) {
// count only non empty subsets
if (taken)
return 1;
return 0;
}
// option 1 -> skip current element
int notTake =
solve(idx + 1,
nums,
usedPrime,
taken);
int take = 0;
int num = nums[idx];
// invalid number can never be used
if (!validNumber(num))
return notTake;
vector<int> primes = getPrimeFactors(num);
bool possible = true;
// check whether any prime already used
for (int p : primes) {
if (usedPrime[p]) {
possible = false;
break;
}
}
// option 2 -> take current element
if (possible) {
// mark primes as used
for (int p : primes)
usedPrime[p]++;
take =
solve(idx + 1,
nums,
usedPrime,
true);
// backtracking step
for (int p : primes)
usedPrime[p]--;
}
return take + notTake;
}
int goodSubsets(vector<int>& nums)
{
map<int,int> usedPrime;
return solve(0, nums, usedPrime, false);
}
int main()
{
vector<int> arr = {1, 2, 3, 4};
cout << goodSubsets(arr);
return 0;
}
import java.util.*;
// Check whether number is square free or not
// Example:
// 6 -> valid
// 12 -> invalid because 2 repeats
class GFG {
static boolean validNumber(int num)
{
for (int i = 2; i * i <= num; i++) {
int cnt = 0;
while (num % i == 0) {
cnt++;
num /= i;
// repeated prime factor found
if (cnt > 1)
return false;
}
}
return true;
}
// Store distinct prime factors
static ArrayList<Integer> getPrimeFactors(int num)
{
ArrayList<Integer> primes = new ArrayList<>();
for (int i = 2; i * i <= num; i++) {
if (num % i == 0) {
primes.add(i);
while (num % i == 0)
num /= i;
}
}
// remaining prime number
if (num > 1)
primes.add(num);
return primes;
}
static int solve(int idx, int[] nums,
HashMap<Integer, Integer> usedPrime,
boolean taken)
{
// all elements processed
if (idx == nums.length) {
// count only non empty subsets
if (taken)
return 1;
return 0;
}
// option 1 -> skip current element
int notTake
= solve(idx + 1, nums, usedPrime, taken);
int take = 0;
int num = nums[idx];
// invalid number can never be used
if (!validNumber(num))
return notTake;
ArrayList<Integer> primes = getPrimeFactors(num);
boolean possible = true;
// check whether any prime already used
for (int p : primes) {
if (usedPrime.getOrDefault(p, 0) > 0) {
possible = false;
break;
}
}
// option 2 -> take current element
if (possible) {
// mark primes as used
for (int p : primes)
usedPrime.put(
p, usedPrime.getOrDefault(p, 0) + 1);
take = solve(idx + 1, nums, usedPrime, true);
// backtracking step
for (int p : primes)
usedPrime.put(p, usedPrime.get(p) - 1);
}
return take + notTake;
}
static int goodSubsets(int[] nums)
{
HashMap<Integer, Integer> usedPrime
= new HashMap<>();
return solve(0, nums, usedPrime, false);
}
public static void main(String[] args)
{
int[] arr = { 1, 2, 3, 4 };
System.out.println(goodSubsets(arr));
}
}
# Check whether number is square free or not
# Example:
# 6 -> valid
# 12 -> invalid because 2 repeats
def validNumber(num):
i = 2
while i * i <= num:
cnt = 0
while num % i == 0:
cnt += 1
num //= i
# repeated prime factor found
if cnt > 1:
return False
i += 1
return True
# Store distinct prime factors
def getPrimeFactors(num):
primes = []
i = 2
while i * i <= num:
if num % i == 0:
primes.append(i)
while num % i == 0:
num //= i
i += 1
# remaining prime number
if num > 1:
primes.append(num)
return primes
def solve(idx,
nums,
usedPrime,
taken):
# all elements processed
if idx == len(nums):
# count only non empty subsets
if taken:
return 1
return 0
# option 1 -> skip current element
notTake = solve(idx + 1,
nums,
usedPrime,
taken)
take = 0
num = nums[idx]
# invalid number can never be used
if not validNumber(num):
return notTake
primes = getPrimeFactors(num)
possible = True
# check whether any prime already used
for p in primes:
if usedPrime.get(p, 0):
possible = False
break
# option 2 -> take current element
if possible:
# mark primes as used
for p in primes:
usedPrime[p] = usedPrime.get(p, 0) + 1
take = solve(idx + 1,
nums,
usedPrime,
True)
# backtracking step
for p in primes:
usedPrime[p] -= 1
return take + notTake
def goodSubsets(nums):
usedPrime = {}
return solve(0, nums, usedPrime, False)
arr = [1, 2, 3, 4]
print(goodSubsets(arr))
using System;
using System.Collections.Generic;
// Check whether number is square free or not
// Example:
// 6 -> valid
// 12 -> invalid because 2 repeats
class GFG {
static bool validNumber(int num)
{
for (int i = 2; i * i <= num; i++) {
int cnt = 0;
while (num % i == 0) {
cnt++;
num /= i;
// repeated prime factor found
if (cnt > 1)
return false;
}
}
return true;
}
// Store distinct prime factors
static List<int> getPrimeFactors(int num)
{
List<int> primes = new List<int>();
for (int i = 2; i * i <= num; i++) {
if (num % i == 0) {
primes.Add(i);
while (num % i == 0)
num /= i;
}
}
// remaining prime number
if (num > 1)
primes.Add(num);
return primes;
}
static int solve(int idx,
int[] nums,
Dictionary<int, int> usedPrime,
bool taken)
{
// all elements processed
if (idx == nums.Length) {
// count only non empty subsets
if (taken)
return 1;
return 0;
}
// option 1 -> skip current element
int notTake =
solve(idx + 1,
nums,
usedPrime,
taken);
int take = 0;
int num = nums[idx];
// invalid number can never be used
if (!validNumber(num))
return notTake;
List<int> primes = getPrimeFactors(num);
bool possible = true;
// check whether any prime already used
foreach (int p in primes) {
if (usedPrime.ContainsKey(p)
&& usedPrime[p] > 0) {
possible = false;
break;
}
}
// option 2 -> take current element
if (possible) {
// mark primes as used
foreach (int p in primes) {
if (!usedPrime.ContainsKey(p))
usedPrime[p] = 0;
usedPrime[p]++;
}
take =
solve(idx + 1,
nums,
usedPrime,
true);
// backtracking step
foreach (int p in primes)
usedPrime[p]--;
}
return take + notTake;
}
static int goodSubsets(int[] nums)
{
Dictionary<int, int> usedPrime
= new Dictionary<int, int>();
return solve(0, nums, usedPrime, false);
}
static void Main()
{
int[] arr = {1, 2, 3, 4};
Console.WriteLine(goodSubsets(arr));
}
}
// Check whether number is square free or not
// Example:
// 6 -> valid
// 12 -> invalid because 2 repeats
function validNumber(num)
{
for (let i = 2; i * i <= num; i++) {
let cnt = 0;
while (num % i === 0) {
cnt++;
num = Math.floor(num / i);
// repeated prime factor found
if (cnt > 1)
return false;
}
}
return true;
}
// Store distinct prime factors
function getPrimeFactors(num)
{
let primes = [];
for (let i = 2; i * i <= num; i++) {
if (num % i === 0) {
primes.push(i);
while (num % i === 0)
num = Math.floor(num / i);
}
}
// remaining prime number
if (num > 1)
primes.push(num);
return primes;
}
function solve(idx,
nums,
usedPrime,
taken)
{
// all elements processed
if (idx === nums.length) {
// count only non empty subsets
if (taken)
return 1;
return 0;
}
// option 1 -> skip current element
let notTake =
solve(idx + 1,
nums,
usedPrime,
taken);
let take = 0;
let num = nums[idx];
// invalid number can never be used
if (!validNumber(num))
return notTake;
let primes = getPrimeFactors(num);
let possible = true;
// check whether any prime already used
for (let p of primes) {
if (usedPrime[p]) {
possible = false;
break;
}
}
// option 2 -> take current element
if (possible) {
// mark primes as used
for (let p of primes) {
if (!usedPrime[p])
usedPrime[p] = 0;
usedPrime[p]++;
}
take =
solve(idx + 1,
nums,
usedPrime,
true);
// backtracking step
for (let p of primes)
usedPrime[p]--;
}
return take + notTake;
}
function goodSubsets(nums)
{
let usedPrime = {};
return solve(0, nums, usedPrime, false);
}
let arr = [1, 2, 3, 4];
console.log(goodSubsets(arr));
Output
7
[Expected Approach] Using Bitmask DP – O(30 × 2¹⁰) Time and O(2¹⁰) Space
As per the question constraints, the array elements are in range from 1 to 30, so the idea is to represent the prime factors of every number using a bitmask. Each bit corresponds to a distinct prime number. Since numbers are limited from
1to30, only the first10primes are needed. A number is valid only if it is square-free. Dynamic Programming is then used to count all possible good subsets while ensuring no prime factor overlaps.
- Count frequency of every number from
1to30 - Generate a prime-factor bitmask for each number
- Ignore numbers having repeated prime factors
- Use
dp[mask]wheremaskrepresents used prime factors - Traverse masks in reverse to avoid overwriting current states
- Add current number only if its mask does not overlap with the existing mask
- Sum all valid states and subtract the empty subset
#include <bits/stdc++.h>
using namespace std;
// Create prime mask for current number
int getMask(int num)
{
vector<int> primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
int mask = 0;
for (int i = 0; i < 10; i++)
{
int p = primes[i];
int cnt = 0;
while (num % p == 0)
{
cnt++;
num /= p;
}
// repeated prime factor
// invalid number
if (cnt > 1)
return -1;
// store current prime in mask
if (cnt == 1)
mask |= (1 << i);
}
return mask;
}
int goodSubsets(vector<int> &nums)
{
int n = nums.size();
// frequency of every number
vector<int> freq(31, 0);
for (int x : nums)
freq[x]++;
// dp[mask]
// mask represents used prime factors
vector<int> dp(1024, 0);
// empty subset
dp[0] = 1;
// process numbers from 2 to 30
for (int num = 2; num <= 30; num++)
{
// number not present
if (freq[num] == 0)
continue;
int currMask = getMask(num);
// invalid number
if (currMask == -1)
continue;
// reverse traversal
// prevents overwriting current states
for (int mask = 1023; mask >= 0; mask--)
{
// overlapping prime factor
// cannot take together
if ((mask & currMask) != 0)
continue;
dp[mask | currMask] += dp[mask] * freq[num];
}
}
int ans = 0;
// add all good subsets
for (int x : dp)
ans += x;
// remove empty subset
return ans - 1;
}
int main()
{
vector<int> arr = {1, 2, 3, 4};
cout << goodSubsets(arr);
return 0;
}
import java.util.*;
class GFG {
// Create prime mask for current number
static int getMask(int num)
{
int[] primes
= { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
int mask = 0;
for (int i = 0; i < 10; i++) {
int p = primes[i];
int cnt = 0;
while (num % p == 0) {
cnt++;
num /= p;
}
// repeated prime factor
// invalid number
if (cnt > 1)
return -1;
// store current prime in mask
if (cnt == 1)
mask |= (1 << i);
}
return mask;
}
static int goodSubsets(int[] nums)
{
int n = nums.length;
// frequency of every number
int[] freq = new int[31];
for (int x : nums)
freq[x]++;
// dp[mask]
// mask represents used prime factors
int[] dp = new int[1024];
// empty subset
dp[0] = 1;
// process numbers from 2 to 30
for (int num = 2; num <= 30; num++) {
// number not present
if (freq[num] == 0)
continue;
int currMask = getMask(num);
// invalid number
if (currMask == -1)
continue;
// reverse traversal
// prevents overwriting current states
for (int mask = 1023; mask >= 0; mask--) {
// overlapping prime factor
// cannot take together
if ((mask & currMask) != 0)
continue;
dp[mask | currMask] += dp[mask] * freq[num];
}
}
int ans = 0;
// add all good subsets
for (int x : dp)
ans += x;
// remove empty subset
return ans - 1;
}
public static void main(String[] args)
{
int[] arr = { 1, 2, 3, 4 };
System.out.println(goodSubsets(arr));
}
}
# Create prime mask for current number
def getMask(num):
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
mask = 0
for i in range(10):
p = primes[i]
cnt = 0
while num % p == 0:
cnt += 1
num //= p
# repeated prime factor
# invalid number
if cnt > 1:
return -1
# store current prime in mask
if cnt == 1:
mask |= (1 << i)
return mask
def goodSubsets(nums):
n = len(nums)
# frequency of every number
freq = [0] * 31
for x in nums:
freq[x] += 1
# dp[mask]
# mask represents used prime factors
dp = [0] * 1024
# empty subset
dp[0] = 1
# process numbers from 2 to 30
for num in range(2, 31):
# number not present
if freq[num] == 0:
continue
currMask = getMask(num)
# invalid number
if currMask == -1:
continue
# reverse traversal
# prevents overwriting current states
for mask in range(1023, -1, -1):
# overlapping prime factor
# cannot take together
if (mask & currMask) != 0:
continue
dp[mask | currMask] += \
dp[mask] * freq[num]
ans = 0
# add all good subsets
for x in dp:
ans += x
# remove empty subset
return ans - 1
arr = [1, 2, 3, 4]
print(goodSubsets(arr))
using System;
class GFG {
// Create prime mask for current number
static int getMask(int num)
{
int[] primes
= { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
int mask = 0;
for (int i = 0; i < 10; i++) {
int p = primes[i];
int cnt = 0;
while (num % p == 0) {
cnt++;
num /= p;
}
// repeated prime factor
// invalid number
if (cnt > 1)
return -1;
// store current prime in mask
if (cnt == 1)
mask |= (1 << i);
}
return mask;
}
static int goodSubsets(int[] nums)
{
int n = nums.Length;
// frequency of every number
int[] freq = new int[31];
foreach(int x in nums) freq[x]++;
// dp[mask]
// mask represents used prime factors
int[] dp = new int[1024];
// empty subset
dp[0] = 1;
// process numbers from 2 to 30
for (int num = 2; num <= 30; num++) {
// number not present
if (freq[num] == 0)
continue;
int currMask = getMask(num);
// invalid number
if (currMask == -1)
continue;
// reverse traversal
// prevents overwriting current states
for (int mask = 1023; mask >= 0; mask--) {
// overlapping prime factor
// cannot take together
if ((mask & currMask) != 0)
continue;
dp[mask | currMask] += dp[mask] * freq[num];
}
}
int ans = 0;
// add all good subsets
foreach(int x in dp) ans += x;
// remove empty subset
return ans - 1;
}
static void Main()
{
int[] arr = { 1, 2, 3, 4 };
Console.WriteLine(goodSubsets(arr));
}
}
// Create prime mask for current number
function getMask(num)
{
let primes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ];
let mask = 0;
for (let i = 0; i < 10; i++) {
let p = primes[i];
let cnt = 0;
while (num % p === 0) {
cnt++;
num = Math.floor(num / p);
}
// repeated prime factor
// invalid number
if (cnt > 1)
return -1;
// store current prime in mask
if (cnt === 1)
mask |= (1 << i);
}
return mask;
}
function goodSubsets(nums)
{
let n = nums.length;
// frequency of every number
let freq = new Array(31).fill(0);
for (let x of nums)
freq[x]++;
// dp[mask]
// mask represents used prime factors
let dp = new Array(1024).fill(0);
// empty subset
dp[0] = 1;
// process numbers from 2 to 30
for (let num = 2; num <= 30; num++) {
// number not present
if (freq[num] === 0)
continue;
let currMask = getMask(num);
// invalid number
if (currMask === -1)
continue;
// reverse traversal
// prevents overwriting current states
for (let mask = 1023; mask >= 0; mask--) {
// overlapping prime factor
// cannot take together
if ((mask & currMask) !== 0)
continue;
dp[mask | currMask] += dp[mask] * freq[num];
}
}
let ans = 0;
// add all good subsets
for (let x of dp)
ans += x;
// remove empty subset
return ans - 1;
}
let arr = [ 1, 2, 3, 4 ];
console.log(goodSubsets(arr));
Output
3