LC 1766. 互质树
题目描述
这是 LeetCode 上的 1766. 互质树 ,难度为 困难。
给你一个 n
个节点的树(也就是一个无环连通无向图),节点编号从 0
到 n - 1
,且恰好有 n - 1
条边,每个节点有一个值,树的根节点为 0
号点。
给你一个整数数组 nums
和一个二维数组 edges
来表示这棵树。
nums[i]
表示第 i
个点的值,$edges[j] = [u{j}, v{j}]$ 表示节点 $u{j}$ 和节点 $v{j}$ 在树中有一条边。
当 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
。
示例 1:
1 |
|
示例 2:
1 |
|
提示:
- $nums.length = n$
- $1 <= nums[i] <= 50$
- $1 <= n <= 10^5$
- $edges.length = n - 1$
- $edges[j].length = 2$
- $0 <= u{j}, v{j} < n$
- $u{j} \neq v{j}$
DFS
题目描述很长,但其实就是说每个节点从下往上找,找到最近的「与其互质」的节点。
数据范围是 $10^5$,如果每个节点都直接往上找最近「互质」祖宗节点的话,当树为线性时,复杂度是 $O(n^2)$ ,会超时。
因此我们要利用 $nums[i]$ 范围只有 $50$ 的特性。
我们可以先预处理除 $[1, 50]$ 范围内的每个数,求出他们互质的数有哪些,存到一个字典里。
那么对于某个节点而言,假设节点的值为 x
,所在层数为 y
。
那么问题转化为求与 x
互质的数有哪些,最近的在哪一层。
用 dep[x]
表示距离值为 x
的节点最近的层是多少;pos[x]
代表具体的节点编号。
代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58class Solution {
int[] ans;
Map<Integer, List<Integer>> map = new HashMap<>(); // 边映射
Map<Integer, List<Integer>> val = new HashMap<>(); // 互质数字典
int[] dep;
int[] pos = new int[52];
public int[] getCoprimes(int[] nums, int[][] edges) {
int n = nums.length;
ans = new int[n];
dep = new int[n];
Arrays.fill(ans, - 1);
Arrays.fill(pos, -1);
for (int[] edge : edges) {
int a = edge[0], b = edge[1];
List<Integer> alist = map.getOrDefault(a, new ArrayList<>());
alist.add(b);
map.put(a, alist);
List<Integer> blist = map.getOrDefault(b, new ArrayList<>());
blist.add(a);
map.put(b, blist);
}
for (int i = 1; i <= 50; i++) {
for (int j = 1; j <= 50; j++) {
if (gcd(i, j) == 1) {
List<Integer> list = val.getOrDefault(i, new ArrayList<>());
list.add(j);
val.put(i, list);
}
}
}
dfs(nums, 0, -1);
return ans;
}
void dfs(int[] nums, int u, int form) {
int t = nums[u];
for (int v : val.get(t)) {
if (pos[v] == -1) continue;
if (ans[u] == -1 || dep[ans[u]] < dep[pos[v]]) ans[u] = pos[v];
}
int p = pos[t];
pos[t] = u;
for (int i : map.get(u)) {
if (i == form) continue;
dep[i] = dep[u] + 1;
dfs(nums, i, u);
}
pos[t] = p;
}
int gcd(int a, int b) {
if (b == 0) return a;
if (a == 0) return b;
return gcd(b, a % b);
}
}
- 时间复杂度:对于每个节点而言,会检查与其数值互质的数有哪些,在哪层。最坏情况下会检查 50 个互质数(当前数值为 1)。复杂度为 $O(n)$
- 空间复杂度:$O(n)$
最后
这是我们「刷穿 LeetCode」系列文章的第 No.1766
篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!