There is a tree (i.e., a connected, undirected graph that has no cycles) consisting of n
nodes numbered from 0
to n - 1
and exactly n - 1
edges. Each node has a value associated with it, and the root of the tree is node 0
.
To represent this tree, you are given an integer array nums
and a 2D array edges
. Each nums[i]
represents the ith
node's value, and each edges[j] = [uj, vj]
represents an edge between nodes uj
and vj
in the tree.
Two values x
and y
are coprime if gcd(x, y) == 1
where gcd(x, y)
is the greatest common divisor of x
and y
.
An ancestor of a node i
is any other node on the shortest path from node i
to the root. A node is not considered an ancestor of itself.
Return an array ans
of size n
, where ans[i]
is the closest ancestor to node i
such that nums[i]
and nums[ans[i]]
are coprime, or -1
if there is no such ancestor.
Example 1:
Input: nums = [2,3,3,2], edges = [[0,1],[1,2],[1,3]] Output: [-1,0,0,1] Explanation: In the above figure, each node's value is in parentheses. - Node 0 has no coprime ancestors. - Node 1 has only one ancestor, node 0. Their values are coprime (gcd(2,3) == 1). - Node 2 has two ancestors, nodes 1 and 0. Node 1's value is not coprime (gcd(3,3) == 3), but node 0's value is (gcd(2,3) == 1), so node 0 is the closest valid ancestor. - Node 3 has two ancestors, nodes 1 and 0. It is coprime with node 1 (gcd(3,2) == 1), so node 1 is its closest valid ancestor.
Example 2:
Input: nums = [5,6,10,2,3,6,15], edges = [[0,1],[0,2],[1,3],[1,4],[2,5],[2,6]] Output: [-1,0,-1,0,0,0,-1]
Constraints:
nums.length == n
1 <= nums[i] <= 50
1 <= n <= 105
edges.length == n - 1
edges[j].length == 2
0 <= uj, vj < n
uj != vj
This problem involves finding the closest ancestor of each node in a tree whose value is coprime with the node's value. The solution leverages Depth-First Search (DFS) and pre-processing to optimize the search.
1. Preprocessing:
2. Depth-First Search (DFS):
The core of the solution is a DFS traversal of the tree. For each node, we perform the following:
i
, we check if any of its ancestors (stored in the stacks corresponding to coprime numbers of nums[i]
) are coprime with nums[i]
. The ancestor with the greatest depth (i.e., closest to node i
) is selected as the closest coprime ancestor.3. Time and Space Complexity:
Time Complexity: The preprocessing step takes O(M^2) time, where M is the maximum value in nums
(50 in this case). The DFS traversal takes O(N) time, where N is the number of nodes in the tree. The search for the closest coprime ancestor within the stacks for each node has a time complexity of O(M), in the worst case. Therefore, the overall time complexity is approximately O(M^2 + N * M).
Space Complexity: The preprocessing step requires O(M^2) space to store the coprime pairs. The DFS uses a stack whose maximum size is proportional to the tree's height (which is at most N). Each node in the tree stores at most M stacks of depth proportional to the height, leading to O(N * M) space complexity for the stacks. The overall space complexity is O(M^2 + N * M).
Code Example (Python):
import math
def gcd(a, b):
if b == 0:
return a
return gcd(b, a % b)
def getCoprimes(nums, edges):
n = len(nums)
graph = [[] for _ in range(n)]
for u, v in edges:
graph[u].append(v)
graph[v].append(u)
coprimes = [[False] * 51 for _ in range(51)] # Precompute coprime pairs
for i in range(1, 51):
for j in range(1, 51):
if gcd(i, j) == 1:
coprimes[i][j] = True
ans = [-1] * n
ancestor_stacks = [[] for _ in range(51)] #Stack for each possible coprime number
def dfs(node, parent, depth):
closest_ancestor = -1
max_depth = -1
for i in range(1, 51):
if coprimes[nums[node]][i]: #Check coprimes of current node value
for anc, d in ancestor_stacks[i]:
if d > max_depth:
max_depth = d
closest_ancestor = anc
ans[node] = closest_ancestor
for child in graph[node]:
if child != parent:
for i in range(1, 51):
if coprimes[nums[node]][i]:
ancestor_stacks[i].append((node,depth))
dfs(child, node, depth + 1)
for i in range(1, 51):
if coprimes[nums[node]][i]:
ancestor_stacks[i].pop()
dfs(0, -1, 0)
return ans
The code in other languages (Java, C++, Go) follows a similar structure, employing DFS and pre-computation of coprime pairs for efficiency. The key difference lies in the implementation details related to data structures (e.g., stacks, adjacency lists).