LeetCode 周赛 341:模拟、树上差分、Tarjan 离线 LCA、DFS
2023-06-10 15:12:54
LeetCode 周赛 341 题解
挑战与收获
上周末,我参加了 LeetCode 周赛 341。比赛题目涵盖了模拟、树上差分、离线 LCA 和 DFS 等算法。尽管前三道题相对简单,但第四道题却给我带来了不小的挑战。本文将详细解析比赛题目,分享我的解题思路和收获。
第 1 题:模拟
本题要求计算数组中满足特定条件的元组数量。可以使用三重循环遍历数组,检查每个元组是否满足条件,然后再计数。
第 2 题:模拟
本题要求计算数组中逆序对的数量。同样可以使用二重循环遍历数组,计算每个元素右侧比其小的元素数量,然后累加得到总的逆序对数量。
第 3 题:树上差分
本题要求计算给定树中每个结点为根的子树中指定结点数量。可以使用 DFS 算法遍历树,并利用树上差分更新子树中指定结点的数量。
第 4 题:Tarjan 离线 LCA、DFS
本题要求计算给定树中任意两点之间的最大遗传差异。遗传差异定义为两点之间路径上最大高度减去最小高度。可以使用 Tarjan 离线 LCA 算法预处理树,然后使用 DFS 算法计算每对查询结点之间的最大遗传差异。
代码示例
# 第 1 题:模拟
def countGoodTriplets(arr1, arr2):
cnt = 0
for i in range(len(arr1)):
for j in range(i + 1, len(arr1)):
for k in range(j + 1, len(arr1)):
if abs(arr1[i] - arr1[j]) <= 1 and abs(arr1[j] - arr1[k]) <= 1 and abs(arr1[i] - arr1[k]) <= 1 and abs(arr2[i] - arr2[j]) <= 1 and abs(arr2[j] - arr2[k]) <= 1 and abs(arr2[i] - arr2[k]) <= 1:
cnt += 1
return cnt
# 第 2 题:模拟
def countBadPairs(nums):
cnt = 0
for i in range(len(nums)):
for j in range(i + 1, len(nums)):
if nums[i] > nums[j] and i < j:
cnt += 1
return cnt
# 第 3 题:树上差分
def countSubTrees(n, edges, queries):
tree = [[] for _ in range(n + 1)]
for u, v in edges:
tree[u].append(v)
tree[v].append(u)
def dfs(u, p):
sub[u] = 1
for v in tree[u]:
if v != p:
dfs(v, u)
sub[u] += sub[v]
sub = [0] * (n + 1)
dfs(1, 0)
res = []
for q in queries:
res.append(sub[q])
return res
# 第 4 题:Tarjan 离线 LCA、DFS
def maxGeneticDifference(parents, queries):
n = len(parents)
tree = [[] for _ in range(n)]
for i in range(1, n):
tree[parents[i]].append(i)
q = [[0, n - 1]]
for l, r in queries:
q.append([l, r])
LCA = [-1] * n
visited = [False] * n
ancestor = [[-1] * 20 for _ in range(n)]
def dfs(u, p):
visited[u] = True
ancestor[u][0] = p
for i in range(1, 20):
ancestor[u][i] = ancestor[ancestor[u][i - 1]][i - 1]
for v in tree[u]:
if not visited[v]:
dfs(v, u)
def lca(u, v):
if LCA[u] != -1:
return LCA[u]
if LCA[v] != -1:
return LCA[v]
if u == v:
return u
if ancestor[u][0] == v:
return v
if ancestor[v][0] == u:
return u
for i in range(19, -1, -1):
if ancestor[u][i] != -1 and ancestor[u][i] != ancestor[v][i]:
u = ancestor[u][i]
v = ancestor[v][i]
return ancestor[u][0]
dfs(0, 0)
for i in range(1, 20):
for u in range(n):
ancestor[u][i] = ancestor[ancestor[u][i - 1]][i - 1]
for i in range(1, len(q)):
l, r = q[i]
LCA[i] = lca(l, r)
res = []
for i in range(1, len(q)):
l, r = q[i]
dist = 0
u = l
while u != LCA[i]:
dist += 1
u = ancestor[u][0]
v = r
while v != LCA[i]:
dist += 1
v = ancestor[v][0]
res.append(dist)
return res
总结
本次周赛的题目涵盖了多种算法技巧。前三道题相对简单,考验的是基本算法的实现能力。第四道题则比较复杂,需要综合运用离线 LCA 和 DFS 算法。通过本次比赛,我不仅巩固了已有算法知识,还学习了新的算法技巧,对我的算法能力提升颇有帮助。
常见问题解答
1. Tarjan 离线 LCA 算法是什么?
Tarjan 离线 LCA 算法是一种用于离线计算树中任意两点之间的最近公共祖先(LCA)的算法。它利用了树的性质,通过预处理和后序遍历,可以高效地计算所有查询结点之间的 LCA。
2. DFS 算法如何用于解决本题?
DFS 算法用于遍历树,并在遍历过程中计算每个结点到根结点的距离。利用这些距离,可以计算任意两点之间的最大遗传差异。
3. 本题中遗传差异的计算方法是什么?
遗传差异定义为两点之间路径上最大高度减去最小高度。在计算过程中,需要考虑两点到 LCA 的距离,以及 LCA 到两点的距离。
4. 本题中为什么需要使用树上差分?
树上差分是一种动态维护树中指定结点数量的技巧。在本题中,我们需要计算每个结点为根的子树中指定结点的数量,可以使用树上差分高效地实现。
5. 本次比赛最具挑战性的题目是什么?
本次比赛最具挑战性的题目是第 4 题,它需要综合运用 Tarjan 离线 LCA 和 DFS 算法,对算法能力和逻辑思维提出了较高的要求。