LC每日一题|20240411 - 1766. 互质树
给你一个
n个节点的树(也就是一个无环连通无向图),节点编号从0到n - 1,且恰好有n - 1条边,每个节点有一个值。树的 根节点 为 0 号点。给你一个整数数组
nums和一个二维数组edges来表示这棵树。nums[i]表示第i个点的值,edges[j] = [uj, vj]表示节点uj和节点vj在树中有一条边。当
gcd(x, y) == 1,我们称两个数x和y是 互质的 ,其中gcd(x, y)是x和y的 最大公约数 。从节点
i到 根 最短路径上的点都是节点i的祖先节点。一个节点 不是 它自己的祖先节点。请你返回一个大小为
n的数组ans,其中ans[i]是离节点i最近的祖先节点且满足nums[i]和nums[ans[i]]是 互质的 ,如果不存在这样的祖先节点,ans[i]为-1。
提示:
nums.length == n1 <= nums[i] <= 501 <= n <= 10^5edges.length == n - 1edges[j].length == 20 <= uj, vj < nuj != vj
题目等级:Hard
前置知识
求取最大公约数,我们需要用到 欧几里得算法,参见 OI-WIKI - 最大公约数 。
它大概长这样,建议背诵全文:
fun gcd(a: Int, b: Int): Int {
if (b == 0) return a
return gcd(b, a % b)
}
解题思路
我们发现 nums[i] 的取值范围非常小,所以我们可以预先处理出互质的数对。
我们可以维护一个数组表示「某一时刻离每个节点值距离最近的互斥节点值的节点号」。在遍历到某一节点时,我们可以将预先处理好的与该节点值互质的节点值刷新为当前节点号。
AC代码
class Solution {
fun getCoprimes(nums: IntArray, edges: Array<IntArray>): IntArray {
//预处理
val arr = Array<ArrayList<Int>>(51) { arrayListOf() }
for (i in 1..50) for (j in 1..50) if (gcd(i, j) == 1) arr[i].add(j)
//构造树
val map = HashMap<Int, ArrayList<Int>>()
edges.forEach {
map[it[0]] = (map[it[0]] ?: arrayListOf()).apply { add(it[1]) }
map[it[1]] = (map[it[1]] ?: arrayListOf()).apply { add(it[0]) }
}
val res = IntArray(nums.size)
//回溯
val c = IntArray(51) { -1 }
fun dfs(cur: Int, parent: Int) {
res[cur] = c[nums[cur]]
arr[nums[cur]].forEach { c[it] = cur } // 刷新状态
map[cur]?.forEach {
if (it != parent) {
val t = IntArray(51)
for (i in t.indices) t[i] = c[i] // 保存案发现场
dfs(it, cur)
for (i in t.indices) c[i] = t[i] // 退回上一步
}
}
}
dfs(0, -1)
return res
}
fun gcd(a: Int, b: Int): Int {
if (b == 0) return a
return gcd(b, a % b)
}
}