Given an undirceted tree consisting of n nodes numbered from 0 to n - 1, each node has an associated value. The task is to compute for each node the nearest ancestor whose value is coprime with the current node's value.
Two numbers are coprime if their greatest common divisor (GCD) is 1.
The solution uses depth-first search (DFS) traversal statring from the root node (0) to explore ancestors efficiently. A precomputed list identifies all values between 1 and 50 that are coprime with each other. During traversal, we maintain the most recent depth and index for each unique node value along the path from root to current node. For each node, we check among its coprime values which one appears closest to it in terms of depth.
const int MAX_VAL = 50;
vector<vector<int>> coprimes(MAX_VAL + 1);
auto initializeCoprimes = []() {
for (int i = 1; i <= MAX_VAL; ++i) {
for (int j = 1; j <= MAX_VAL; ++j) {
if (__gcd(i, j) == 1) {
coprimes[i].push_back(j);
}
}
}
return 0;
}();
class Solution {
private:
vector<vector<int>> adjacencyList;
vector<int> result;
pair<int, int> lastSeen[MAX_VAL + 1];
void traverse(int currentNode, int parentNode, int currentDepth, const vector<int>& nodeValues) {
int nodeValue = nodeValues[currentNode];
int deepestMatch = -1;
int matchedAncestor = -1;
for (int candidate : coprimes[nodeValue]) {
auto [ancestorDepth, ancestorIndex] = lastSeen[candidate];
if (ancestorDepth > deepestMatch) {
deepestMatch = ancestorDepth;
matchedAncestor = ancestorIndex;
}
}
result[currentNode] = matchedAncestor;
auto backup = lastSeen[nodeValue];
lastSeen[nodeValue] = {currentDepth, currentNode};
for (int neighbor : adjacencyList[currentNode]) {
if (neighbor != parentNode) {
traverse(neighbor, currentNode, currentDepth + 1, nodeValues);
}
}
lastSeen[nodeValue] = backup;
}
public:
vector<int> getCoprimes(vector<int>& nums, vector<vector<int>>& edges) {
int n = nums.size();
adjacencyList.assign(n, {});
for (const auto& edge : edges) {
int u = edge[0], v = edge[1];
adjacencyList[u].push_back(v);
adjacencyList[v].push_back(u);
}
result.assign(n, -1);
traverse(0, -1, 1, nums);
return result;
}
};
The algorithm ensures efficient computation by leveraging fixed-range constraints on node values (up to 50), allowing precomputation of coprime relationships. Each DFS call updates and restores state information to correctly track ancestor depths.