823. Binary Trees With Factors #
Problem #
Given an array of unique integers, arr, where each integer arr[i] is strictly greater than 1.
We make a binary tree using these integers, and each number may be used for any number of times. Each non-leaf node’s value should be equal to the product of the values of its children.
Return the number of binary trees we can make. The answer may be too large so return the answer modulo 109 + 7.
Example 1:
Input: arr = [2,4]
Output: 3
Explanation: We can make these trees: [2], [4], [4, 2, 2]
Example 2:
Input: arr = [2,4,5,10]
Output: 7
Explanation: We can make these trees: [2], [4], [5], [10], [4, 2, 2], [10, 2, 5], [10, 5, 2].
Constraints:
1 <= arr.length <= 10002 <= arr[i] <= 10^9
Problem Summary #
Given an array containing unique integer elements, each integer is greater than 1. We use these integers to construct binary trees, and each integer can be used any number of times. In such a tree, the value of each non-leaf node should be equal to the product of the values of its two child nodes. How many binary trees satisfy the conditions? The returned result should be modulo 10 * 9 + 7.
Solution Ideas #
The first thought is a brute-force solution: sort first, then traverse all nodes and enumerate combinations where the product of two numbers equals the value of a third node. Then enumerate these combinations and form trees. When counting here, note that if the left and right children are not symmetric, swapping the left and right subtrees is another solution. But this method times out. The reason is that brute force enumerates many repeated nodes and combinations. The way to optimize this is to put already calculated nodes into a
map. There are 2 layers ofmaphere. The first layer ofmapmemoizes combinations of pairwise products, using the parent node as thekeyand the 2 left and right children as thevalue. The second layer ofmapmemoizes the number of types of binary trees withrootas the root node at this time; thekeyisroot, and thevaluestores the number of types. After this optimization, the DFS brute-force solution can runtime beats 100%.Another solution is DP. Define
\[ dp[i] = \sum_{j<i, k<i}^{}dp[j] * dp[k], j * k = i \]dp[i]as the number of types of trees withias the root node. dp[i] is initially 1, because every node itself can form a tree with itself as the single noderoot. Sorting is also needed first. The state transition equation is:Finally, accumulate all results in the
dp[]array and take the modulo to get the final result. The time complexity is O(n^2), and the space complexity is O(n).
Code #
package leetcode
import (
"sort"
)
const mod = 1e9 + 7
// Solution 1 DFS
func numFactoredBinaryTrees(arr []int) int {
sort.Ints(arr)
numDict := map[int]bool{}
for _, num := range arr {
numDict[num] = true
}
dict, res := make(map[int][][2]int), 0
for i, num := range arr {
for j := i; j < len(arr) && num*arr[j] <= arr[len(arr)-1]; j++ {
tmp := num * arr[j]
if !numDict[tmp] {
continue
}
dict[tmp] = append(dict[tmp], [2]int{num, arr[j]})
}
}
cache := make(map[int]int)
for _, num := range arr {
res = (res + dfs(num, dict, cache)) % mod
}
return res
}
func dfs(num int, dict map[int][][2]int, cache map[int]int) int {
if val, ok := cache[num]; ok {
return val
}
res := 1
for _, tuple := range dict[num] {
a, b := tuple[0], tuple[1]
x, y := dfs(a, dict, cache), dfs(b, dict, cache)
tmp := x * y
if a != b {
tmp *= 2
}
res = (res + tmp) % mod
}
cache[num] = res
return res
}
// Solution 2 DP
func numFactoredBinaryTrees1(arr []int) int {
dp := make(map[int]int)
sort.Ints(arr)
for i, curNum := range arr {
for j := 0; j < i; j++ {
factor := arr[j]
quotient, remainder := curNum/factor, curNum%factor
if remainder == 0 {
dp[curNum] += dp[factor] * dp[quotient]
}
}
dp[curNum]++
}
totalCount := 0
for _, count := range dp {
totalCount += count
}
return totalCount % mod
}