E - 最も深い共通の上司 / Deepest Common Boss 解説 by admin
gpt-5.3-codexOverview
The “deepest common supervisor shared by two people” asked for in each query is exactly the LCA (Lowest Common Ancestor) in a tree.
To process this efficiently, we precompute ancestors using Binary Lifting (Doubling) and answer each query in \(O(\log N)\).
Analysis
This problem can be modeled as a rooted tree (with root 1), where employees are vertices and direct supervisor relationships are edges.
The “deepest common supervisor” of employees \(X\) and \(Y\) is precisely the LCA in the tree.
Why a naive approach is too slow
For example, if for each query we: - Traverse all ancestors of \(X\) - Walk upward from \(Y\) to find the first matching ancestor
this takes \(O(N)\) per query in the worst case.
Since \(Q\) is up to \(5 \times 10^4\) and \(N\) is up to \(3 \times 10^5\), an \(O(NQ)\) approach will not be fast enough.
How to speed it up
We use binary lifting, a standard technique for LCA.
For each vertex \(v\), we precompute:
up[0][v]= the parent 1 level aboveup[1][v]= the ancestor 2 levels aboveup[2][v]= the ancestor 4 levels above- …
up[k][v]= the ancestor \(2^k\) levels above
With this precomputation,
both “closing the depth gap” and “moving both nodes upward simultaneously” can be done bit by bit, resulting in \(O(\log N)\) per query.
Algorithm
Precomputation (depth and parent)
- Due to the input condition \(P_i < i\), processing vertices 2..N in order guarantees that each parent’s information is already determined.
- Compute depth as
depth[v] = depth[parent] + 1. - Set
up[0][v] = parent. - For root 1, set
up[0][1] = 1(as a sentinel, making it its own parent).
Building the binary lifting table
up[k][v] = up[k-1][ up[k-1][v] ]- This gives us the \(2^k\)-th ancestor.
LCA query
- Let
bbe the shallower node andabe the deeper node (swap if necessary). - Compute the depth difference
d = depth[a] - depth[b], and for each set bit ind, applya = up[bit][a]to close the gap. - If
a == bat this point, that is the LCA (the case where one node is an ancestor of the other). - Otherwise, iterate
kfrom the largest value downward: whenup[k][a] != up[k][b], move both nodes upward simultaneously. - Finally, the parent one level above,
up[0][a], is the LCA.
- Let
Complexity
- Time complexity: \(O(N \log N + Q \log N)\)
- Space complexity: \(O(N \log N)\)
Implementation Notes
Use
LOG = N.bit_length()to determine the required number of levels (approximately \(\lceil \log_2 N \rceil\)).Setting the root’s parent to
1helps prevent out-of-bounds array access and makes the implementation more robust.In this code, DFS/BFS is not needed to compute depths (leveraging the property \(P_i < i\)).
Collecting output in a
listand printing with"\n".join(out)at the end is faster.Source Code
import sys
def main():
input = sys.stdin.readline
N, Q = map(int, input().split())
P = [0] + [0] + list(map(int, input().split())) # P[i] for i>=2
LOG = (N).bit_length()
up = [[0] * (N + 1) for _ in range(LOG)]
depth = [0] * (N + 1)
up[0][1] = 1
for v in range(2, N + 1):
p = P[v]
up[0][v] = p
depth[v] = depth[p] + 1
for k in range(1, LOG):
prev = up[k - 1]
cur = up[k]
for v in range(1, N + 1):
cur[v] = prev[prev[v]]
def lca(a, b):
if depth[a] < depth[b]:
a, b = b, a
d = depth[a] - depth[b]
bit = 0
while d:
if d & 1:
a = up[bit][a]
d >>= 1
bit += 1
if a == b:
return a
for k in range(LOG - 1, -1, -1):
if up[k][a] != up[k][b]:
a = up[k][a]
b = up[k][b]
return up[0][a]
out = []
for _ in range(Q):
x, y = map(int, input().split())
out.append(str(lca(x, y)))
sys.stdout.write("\n".join(out))
if __name__ == "__main__":
main()
This editorial was generated by gpt-5.3-codex.
投稿日時:
最終更新: