class Solution:
def numFactoredBinaryTrees(self, arr):
MOD = 10 ** 9 + 7
arr.sort()
dp = {}
index = {x: i for i, x in enumerate(arr)}
for i, x in enumerate(arr):
dp[x] = 1
for j in range(i):
if x % arr[j] == 0:
right = x // arr[j]
if right in dp:
dp[x] += dp[arr[j]] * dp[right]
dp[x] %= MOD
return sum(dp.values()) % MOD
class Solution {
public:
int numFactoredBinaryTrees(vector<int>& arr) {
const int MOD = 1e9 + 7;
sort(arr.begin(), arr.end());
unordered_map<int, long> dp;
for (int i = 0; i < arr.size(); ++i) {
dp[arr[i]] = 1;
for (int j = 0; j < i; ++j) {
if (arr[i] % arr[j] == 0) {
int right = arr[i] / arr[j];
if (dp.count(right)) {
dp[arr[i]] = (dp[arr[i]] + dp[arr[j]] * dp[right]) % MOD;
}
}
}
}
long res = 0;
for (auto& p : dp) res = (res + p.second) % MOD;
return (int)res;
}
};
class Solution {
public int numFactoredBinaryTrees(int[] arr) {
int MOD = 1_000_000_007;
Arrays.sort(arr);
Map<Integer, Long> dp = new HashMap<>();
for (int i = 0; i < arr.length; ++i) {
dp.put(arr[i], 1L);
for (int j = 0; j < i; ++j) {
if (arr[i] % arr[j] == 0) {
int right = arr[i] / arr[j];
if (dp.containsKey(right)) {
dp.put(arr[i], (dp.get(arr[i]) + dp.get(arr[j]) * dp.get(right)) % MOD);
}
}
}
}
long res = 0;
for (long v : dp.values()) res = (res + v) % MOD;
return (int)res;
}
}
var numFactoredBinaryTrees = function(arr) {
const MOD = 1e9 + 7;
arr.sort((a, b) => a - b);
const dp = new Map();
for (let i = 0; i < arr.length; ++i) {
dp.set(arr[i], 1);
for (let j = 0; j < i; ++j) {
if (arr[i] % arr[j] === 0) {
let right = arr[i] / arr[j];
if (dp.has(right)) {
dp.set(arr[i], (dp.get(arr[i]) + dp.get(arr[j]) * dp.get(right)) % MOD);
}
}
}
}
let res = 0;
for (let v of dp.values()) res = (res + v) % MOD;
return res;
};
Given an array arr
of unique positive integers, consider binary trees where each non-leaf node's value is the product of its two children's values, and each node's value must be in arr
. Each integer from arr
can be used multiple times in different trees or positions. The task is to count the number of possible binary trees that can be built, where the value of each node is from arr
and for every non-leaf node, its value equals the product of its two children (both from arr
). Return the count modulo 109+7.
arr
.
At first, you might think about brute-forcing all possible tree structures, recursively trying to pair every possible combination of left and right children for each value. However, this quickly becomes infeasible as the number of possible trees grows exponentially with the size of arr
.
To optimize, notice that for each value x
in arr
, you can build trees with x
as the root by finding all pairs of values (a, b)
in arr
such that a * b == x
. For each such pair, the number of ways to build a tree with a
as the left child and b
as the right child is the product of the number of trees rooted at a
and the number rooted at b
.
This suggests a dynamic programming approach: for each value, store the number of trees that can be built with it as the root, and build up from the smallest to the largest values.
We use dynamic programming to efficiently count the number of binary trees for each value in arr
. Here’s how:
arr
in ascending order so we can build up solutions from smaller factors.dp
where dp[x]
stores the number of binary trees with root x
.x
in arr
:
dp[x] = 1
(a single node tree).a
in arr
(where a < x
):
x % a == 0
, then b = x / a
is a potential right child.b
is in arr
, then a
and b
can be children of x
.dp[a] * dp[b]
to dp[x]
(since left and right subtrees are independent).dp[x]
for all x
in arr
for the final answer.
We use a hash map for dp
so that lookups and updates are O(1). Sorting ensures we only consider smaller factors first, so all needed subproblem results are already computed.
Let's take arr = [2, 4, 5, 10]
.
dp[2] = 1
.arr
.dp[4] = 1
(single node) + dp[2]*dp[2]
= 1+1=2.dp[5] = 1
.dp[10] = 1
(single node) + dp[2]*dp[5]
+ dp[5]*dp[2]
= 1+1+1=3.This matches the expected output for this input.
x
in arr
, we check all smaller elements as possible left children. For each, we do O(1) work (lookup and multiplication).
arr
.
dp
map.
The key insight is to recognize the subproblem structure: the number of trees for a value depends on the number of trees for its possible factors. By sorting the array and using dynamic programming, we efficiently build up the answer for each value, ensuring all required sub-results are available when needed. This approach reduces the problem from exponential to quadratic time, making it feasible for large inputs. The solution is elegant because it leverages the properties of binary trees and factorization, and uses memoization to avoid redundant computation.