Given an array A of n integers.
A sequence B is called a subsequence of A if it can be obtained by deleting zero or more elements from A without changing the order of the remaining elements.
A subsequence B = [b₁, b₂, …, bₖ] is called good if:
The subsequence is non-empty and for every position i (1-indexed), bᵢ is divisible by i
The task is to find the total number of good subsequences of the array A.
Since the result can be large, output the answer modulo 10^9 + 7.
Example:
Input : n = 5, a[] = [2, 2, 1, 22, 14]
Output : 13
Explanation : The possible good subsequences
are: {2}, {2,2}, {2,22}, {2,14}, {2}, {2,22}, {2,14},
{1}, {1,22}, {1,14}, {22}, {22,14}, {14}.
Note, that some subsequences are listed more than once,
since they occur in the original array multiple times.
Table of Content
[Naive Approach] Generating All Subsequences O(N * 2^N) Time and O(N · 2^N) Space
In this approach, we generate all possible subsequences of the given array. Each element can either be included or excluded, resulting in 2^N total subsequences. For every non-empty subsequence, we verify whether the element at position i is divisible by i. If the condition is satisfied for all positions, the subsequence is counted as good. Although intuitive, this approach is inefficient because of its exponential time complexity
#include <bits/stdc++.h>
using namespace std;
bool isGood(vector<int>& b) {
for (int i = 0; i < b.size(); i++) {
if (b[i] % (i + 1) != 0) return false;
}
return true;
}
int countGood(int idx, vector<int>& a, vector<int>& cur) {
if (idx == a.size()) {
// empty not allowed
if (cur.empty()) return 0;
return isGood(cur) ? 1 : 0;
}
// skip
int ans = countGood(idx + 1, a, cur);
// take
cur.push_back(a[idx]);
ans += countGood(idx + 1, a, cur);
cur.pop_back();
return ans;
}
int main() {
vector<int> a = {2, 2, 1, 22, 14};
vector<int> cur;
cout << countGood(0, a, cur);
return 0;
}
import java.util.*;
class GFG {
static boolean isGood(List<Integer> b) {
for (int i = 0; i < b.size(); i++) {
if (b.get(i) % (i + 1) != 0) return false;
}
return true;
}
static int countGood(int idx, int[] a, List<Integer> cur) {
if (idx == a.length) {
// empty not allowed
if (cur.isEmpty()) return 0;
return isGood(cur) ? 1 : 0;
}
// skip
int ans = countGood(idx + 1, a, cur);
// take
cur.add(a[idx]);
ans += countGood(idx + 1, a, cur);
cur.remove(cur.size() - 1);
return ans;
}
public static void main(String[] args) {
int[] a = {2, 2, 1, 22, 14};
System.out.println(countGood(0, a, new ArrayList<>()));
}
}
def is_good(b):
for i in range(len(b)):
if b[i] % (i + 1) != 0:
return False
return True
def count_good(idx, a, cur):
if idx == len(a):
if not cur:
return 0
return 1 if is_good(cur) else 0
# skip
ans = count_good(idx + 1, a, cur)
# take
cur.append(a[idx])
ans += count_good(idx + 1, a, cur)
cur.pop()
return ans
if __name__ == "__main__":
a = [2, 2, 1, 22, 14]
print(count_good(0, a, []))
using System;
using System.Collections.Generic;
class GFG {
static bool IsGood(List<int> b) {
for (int i = 0; i < b.Count; i++) {
if (b[i] % (i + 1) != 0) return false;
}
return true;
}
static int CountGood(int idx, int[] a, List<int> cur) {
if (idx == a.Length) {
if (cur.Count == 0) return 0;
return IsGood(cur) ? 1 : 0;
}
// skip
int ans = CountGood(idx + 1, a, cur);
// take
cur.Add(a[idx]);
ans += CountGood(idx + 1, a, cur);
cur.RemoveAt(cur.Count - 1);
return ans;
}
static void Main() {
int[] a = {2, 2, 1, 22, 14};
Console.WriteLine(CountGood(0, a, new List<int>()));
}
}
function isGood(b) {
for (let i = 0; i < b.length; i++) {
if (b[i] % (i + 1) !== 0) return false;
}
return true;
}
function countGood(idx, a, cur) {
if (idx === a.length) {
if (cur.length === 0) return 0;
return isGood(cur) ? 1 : 0;
}
// skip
let ans = countGood(idx + 1, a, cur);
// take
cur.push(a[idx]);
ans += countGood(idx + 1, a, cur);
cur.pop();
return ans;
}
//Driver Code
const a = [2, 2, 1, 22, 14];
console.log(countGood(0, a, []));
Output
13
[Better Approach] Memoization — O(n²) Time, O(n²) Space
In this approach, we use memoization to avoid recomputing overlapping subproblems.
The DP state is defined as dp[idx][len], representing the number of good subsequences that can be formed using elements from index idx onward with current subsequence length len.
The recurrence is:
dp(idx, len) = dp(idx+1, len) + dp(idx+1, len+1) if a[idx] % (len+1) == 0The base case returns 1 for non-empty subsequences and 0 otherwise.
#include <bits/stdc++.h>
using namespace std;
int mod = 1000000007;
int countGoodSubsequences(int idx, int len, vector<int>& arr, vector<vector<int>>& dp) {
if (idx == arr.size())
return (len > 0) ? 1 : 0;
if (dp[idx][len] != -1)
return dp[idx][len];
// skip current element
long long ans = countGoodSubsequences(idx + 1, len, arr, dp);
// take current element if valid
if (arr[idx] % (len + 1) == 0)
ans += countGoodSubsequences(idx + 1, len + 1, arr, dp);
return dp[idx][len] = ans % mod;
}
int main() {
vector<int> arr = {2, 2, 1, 22, 14};
int n = arr.size();
vector<vector<int>> dp(n + 1, vector<int>(n + 1, -1));
cout << countGoodSubsequences(0, 0, arr, dp);
return 0;
}
import java.util.*;
class GFG {
static final int MOD = 1000000007;
static int[][] dp;
static int[] arr;
static int countGoodSubsequences(int idx, int len)
{
if (idx == arr.length)
return (len > 0) ? 1 : 0;
if (dp[idx][len] != -1)
return dp[idx][len];
// skip
long ans = countGoodSubsequences(idx + 1, len);
if (arr[idx] % (len + 1) == 0) {
// take
ans += countGoodSubsequences(idx + 1, len + 1);
}
return dp[idx][len] = (int)(ans % MOD);
}
public static void main(String[] args)
{
arr = new int[] { 2, 2, 1, 22, 14 };
int n = arr.length;
dp = new int[n + 1][n + 1];
for (int[] row : dp)
Arrays.fill(row, -1);
System.out.println(countGoodSubsequences(0, 0));
}
}
def countGoodSubsequences(idx, length, arr, dp):
if idx == len(arr):
return 1 if length > 0 else 0
if dp[idx][length] != -1:
return dp[idx][length]
# skip current element
ans = countGoodSubsequences(idx + 1, length, arr, dp)
# take current element if valid
if arr[idx] % (length + 1) == 0:
ans += countGoodSubsequences(idx + 1, length + 1, arr, dp)
dp[idx][length] = ans
return ans
if __name__ == "__main__":
arr = [2, 2, 1, 22, 14]
n = len(arr)
dp = [[-1] * (n + 1) for _ in range(n + 1)]
print(countGoodSubsequences(0, 0, arr, dp))
using System;
using System.Collections.Generic;
class Program {
static int mod = 1000000007;
static int[, ] dp;
static int[] arr;
static int CountGoodSubsequences(int idx, int len)
{
if (idx == arr.Length)
return (len > 0) ? 1 : 0;
if (dp[idx, len] != -1)
return dp[idx, len];
// skip
long ans = CountGoodSubsequences(idx + 1, len);
if (arr[idx] % (len + 1) == 0) {
// take
ans += CountGoodSubsequences(idx + 1, len + 1);
}
return dp[idx, len] = (int)(ans % mod);
}
static void Main()
{
arr = new int[] { 2, 2, 1, 22, 14 };
int n = arr.Length;
dp = new int[n + 1, n + 1];
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++)
dp[i, j] = -1;
Console.WriteLine(CountGoodSubsequences(0, 0));
}
}
const mod = 1000000007;
function countGoodSubsequences(idx, len, arr, dp) {
if (idx === arr.length)
return len > 0 ? 1 : 0;
if (dp[idx][len] !== -1)
return dp[idx][len];
// skip current element
let ans = countGoodSubsequences(idx + 1, len, arr, dp);
// take current element if valid
if (arr[idx] % (len + 1) === 0)
ans += countGoodSubsequences(idx + 1, len + 1, arr, dp);
dp[idx][len] = ans % mod;
return dp[idx][len];
}
//Driver Code
function main() {
let arr = [2, 2, 1, 22, 14];
let n = arr.length;
let dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(-1));
console.log(countGoodSubsequences(0, 0, arr, dp));
}
main();
Output
13
[Better Approach] Tabulation — O(n²) Time, O(n) Space
In this approach, we use bottom-up dynamic programming. Let dp[len] represent the number of good subsequences of length len. Initially, dp[0] = 1 to help build subsequences. For each element, we try to place it at a valid subsequence position len such that a[i] % len == 0. We update dp in reverse order to avoid reuse of the same element.
#include <bits/stdc++.h>
using namespace std;
int countGoodSubsequences(vector<int>& arr) {
int n = arr.size();
const int mod = 1000000007;
vector<long long> dp(n + 1, 0);
dp[0] = 1;
for (int x : arr) {
for (int len = n; len >= 1; len--) {
if (x % len == 0) {
dp[len] = (dp[len] + dp[len - 1]) % mod;
}
}
}
long long ans = 0;
for (int len = 1; len <= n; len++) ans = (ans + dp[len]) % mod;
return ans;
}
int main() {
vector<int> arr = {2, 2, 1, 22, 14};
cout << countGoodSubsequences(arr);
return 0;
}
import java.util.*;
class GFG {
static final int MOD = 1000000007;
static int countGoodSubsequences(int[] arr) {
int n = arr.length;
long[] dp = new long[n + 1];
dp[0] = 1;
for (int x : arr) {
for (int len = n; len >= 1; len--) {
if (x % len == 0) {
dp[len] = (dp[len] + dp[len - 1]) % MOD;
}
}
}
long ans = 0;
for (int len = 1; len <= n; len++) ans = (ans + dp[len]) % MOD;
return (int) ans;
}
public static void main(String[] args) {
int[] arr = {2, 2, 1, 22, 14};
System.out.println(countGoodSubsequences(arr));
}
}
def countGoodSubsequences(arr):
n = len(arr)
mod = 1000000007
dp = [0] * (n + 1)
dp[0] = 1
for x in arr:
for length in range(n, 0, -1):
if x % length == 0:
dp[length] = (dp[length] + dp[length - 1]) % mod
return sum(dp[1:]) % mod
if __name__ == "__main__":
arr = [2, 2, 1, 22, 14]
print(countGoodSubsequences(arr))
using System;
class GFG {
static int CountGoodSubsequences(int[] arr) {
int n = arr.Length;
const int mod = 1000000007;
long[] dp = new long[n + 1];
dp[0] = 1;
foreach (int x in arr) {
for (int len = n; len >= 1; len--) {
if (x % len == 0) {
dp[len] = (dp[len] + dp[len - 1]) % mod;
}
}
}
long ans = 0;
for (int len = 1; len <= n; len++) ans = (ans + dp[len]) % mod;
return (int) ans;
}
static void Main() {
int[] arr = {2, 2, 1, 22, 14};
Console.WriteLine(CountGoodSubsequences(arr));
}
}
const MOD = 1000000007;
function countGoodSubsequences(arr) {
const n = arr.length;
const dp = Array(n + 1).fill(0);
dp[0] = 1;
for (const x of arr) {
for (let len = n; len >= 1; len--) {
if (x % len === 0) {
dp[len] = (dp[len] + dp[len - 1]) % MOD;
}
}
}
let ans = 0;
for (let len = 1; len <= n; len++) ans = (ans + dp[len]) % MOD;
return ans;
}
// Driver Code
function main() {
const arr = [2, 2, 1, 22, 14];
console.log(countGoodSubsequences(arr));
}
main();
Output
13