Bỏ qua để đến nội dung

Tổ tiên chung gần nhất (LCA)

Mức độ: Chuyên sâu

Trên một cây có gốc, tổ tiên của u là các đỉnh trên đường từ u đi lên gốc (gồm cả u). Tổ tiên chung gần nhất (LCA) của u và v là tổ tiên chung sâu nhất của cả hai, cũng là chỗ hai đường đi lên gốc gặp nhau. Đường đi giữa u và v trên cây luôn đi lên tới LCA rồi đi xuống, nên LCA trả lời được rất nhiều câu hỏi về đường đi: khoảng cách, cạnh lớn nhất, đỉnh thứ k…

Đưa đỉnh sâu hơn đi lên cho tới khi hai đỉnh cùng độ sâu, rồi cho cả hai cùng đi lên từng bước tới khi gặp nhau. Mỗi câu hỏi có thể mất n bước (cây là một đường dài), với 105 câu hỏi là 1010.

Như nhảy k bước, lưu len[j][v] là tổ tiên thứ 2j của v (đi lên 2j bước): len[0][v] là cha, len[j][v] = len[j - 1][len[j - 1][v]].

Tìm LCA của u và v (giả sử u sâu hơn):

  1. Đưa về cùng độ sâu: u cần đi lên d = sau[u] - sau[v] bước. Viết d dạng nhị phân, bit thứ j bằng 1 thì u = len[j][u].
  2. Nếu lúc này u = v thì v là LCA.
  3. Nhảy cùng lúc: đi j từ lớn đến nhỏ; nếu len[j][u] khác len[j][v] thì nhảy cả hai (u = len[j][u], v = len[j][v]). Nhảy như vậy hai đỉnh vẫn ở dưới LCA. Kết thúc, u và v là hai con của LCA, đáp số là cha của u.

Ở bước 3, nếu len[j][u] = len[j][v] thì đó là một tổ tiên chung, có thể đã vượt quá LCA, nên không nhảy. Cuối cùng u, v đứng ngay dưới LCA: chỉ còn thiếu một bước.

Mỗi câu hỏi khoảng 2 × log2n bước, chuẩn bị bảng mất n × log2n.

import sys
du_lieu = sys.stdin.read().split()
n, q = int(du_lieu[0]), int(du_lieu[1])
ke = [[] for _ in range(n + 1)]
for i in range(n - 1):
u, v = int(du_lieu[2 + 2 * i]), int(du_lieu[3 + 2 * i])
ke[u].append(v)
ke[v].append(u)
LOG = max(1, n.bit_length())
sau = [0] * (n + 1)
len_ = [[0] * (n + 1) for _ in range(LOG)] # len_[j][v]: tổ tiên thứ 2^j
thu_tu, da_tham = [1], [False] * (n + 1)
da_tham[1] = True
for u in thu_tu: # BFS từ gốc 1
for v in ke[u]:
if not da_tham[v]:
da_tham[v] = True
sau[v] = sau[u] + 1
len_[0][v] = u
thu_tu.append(v)
len_[0][1] = 1 # cha của gốc là chính nó
for j in range(1, LOG):
truoc, hien = len_[j - 1], len_[j]
for v in range(1, n + 1):
hien[v] = truoc[truoc[v]]
def lca(u, v):
if sau[u] < sau[v]:
u, v = v, u
d, j = sau[u] - sau[v], 0
while d: # đưa u lên cùng độ sâu với v
if d & 1:
u = len_[j][u]
d >>= 1
j += 1
if u == v:
return u
for j in range(LOG - 1, -1, -1): # nhảy cùng lúc, luôn ở dưới LCA
if len_[j][u] != len_[j][v]:
u, v = len_[j][u], len_[j][v]
return len_[0][u]
vt = 2 + 2 * (n - 1)
print("\n".join(str(lca(int(du_lieu[vt + 2 * t]), int(du_lieu[vt + 2 * t + 1]))) for t in range(q)))

Gọi w = LCA(u, v):

Câu hỏiCách tính
số cạnh giữa u và vsau[u] + sau[v] - 2 × sau[w]
tổng độ dài (cây có trọng số)kc[u] + kc[v] - 2 × kc[w], với kc là khoảng cách từ gốc
x có nằm trên đường u - v khôngkhoảng cách(u, x) + khoảng cách(x, v) = khoảng cách(u, v)
cạnh lớn nhất trên đườnglưu thêm lon[j][v] là cạnh lớn nhất khi nhảy 2j bước, lấy max khi nhảy
cộng 1 cho mọi đỉnh trên đường u - vcộng 1 tại u và v, trừ 1 tại w và cha của w, rồi cộng dồn từ lá lên gốc

Gồm 12 bài, chia thành 4 nhóm từ dễ đến khó. Mỗi bài có một dữ liệu kiểm tra: các bạn chạy chương trình của mình với dữ liệu đó rồi so với đáp số.

Cây có n đỉnh, gốc là đỉnh 1. Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), n - 1 dòng sau là các cạnh, q dòng sau mỗi dòng có u, v. Với mỗi câu hỏi, in ra tổ tiên chung gần nhất của u và v.

Ví dụ:

NhậpIn ra
7 4
1 2
1 3
2 4
2 5
5 6
3 7
4 6
6 7
5 5
2 6
2
1
5
2

Kiểm tra: nhập

10 5
1 2
2 3
3 4
2 5
5 6
1 7
7 8
7 9
9 10
4 6
10 8
4 10
3 3
6 5
Xem đáp số
2
7
1
3
5

Hướng dẫn giải chi tiết: liên hệ tác giả.

Cây có n đỉnh. Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), n - 1 dòng sau là các cạnh, q dòng sau mỗi dòng có u, v. Với mỗi câu hỏi, in ra số cạnh trên đường đi giữa u và v.

Ví dụ:

NhậpIn ra
7 3
1 2
1 3
2 4
2 5
5 6
3 7
4 6
6 7
1 1
3
5
0

Kiểm tra: nhập

10 4
1 2
2 3
3 4
2 5
5 6
1 7
7 8
7 9
9 10
4 6
10 8
4 10
1 10
Xem đáp số
4
3
6
3

Hướng dẫn giải chi tiết: liên hệ tác giả.

Cây có n đỉnh, mỗi cạnh có độ dài. Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), n - 1 dòng sau mỗi dòng có u, v, w (1 ≤ w ≤ 109), q dòng sau mỗi dòng có u, v. Với mỗi câu hỏi, in ra tổng độ dài đường đi giữa u và v.

Ví dụ:

NhậpIn ra
5 3
1 2 3
1 3 4
3 4 2
3 5 6
2 4
4 5
1 1
9
8
0

Kiểm tra: nhập

8 4
1 2 5
2 3 1
2 4 7
1 5 2
5 6 9
6 7 3
5 8 8
3 7
4 8
6 6
1 7
Xem đáp số
20
22
0
14

Hướng dẫn giải chi tiết: liên hệ tác giả.

Cây có n đỉnh. Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), n - 1 dòng sau là các cạnh, q dòng sau mỗi dòng có u, v, x. Với mỗi câu hỏi, in ra YES nếu đỉnh x nằm trên đường đi giữa u và v (kể cả hai đầu), ngược lại NO.

Ví dụ:

NhậpIn ra
7 3
1 2
1 3
2 4
2 5
5 6
3 7
4 6 2
4 6 1
6 7 3
YES
NO
YES

Kiểm tra: nhập

10 4
1 2
2 3
3 4
2 5
5 6
1 7
7 8
7 9
9 10
4 6 5
4 10 1
8 10 9
3 3 2
Xem đáp số
YES
YES
YES
NO

Hướng dẫn giải chi tiết: liên hệ tác giả.

Cây có n đỉnh. Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), n - 1 dòng sau là các cạnh, q dòng sau mỗi dòng có a, b, c: vị trí của ba người. Với mỗi câu hỏi, chọn một đỉnh để ba người cùng đi tới sao cho tổng số cạnh ba người phải đi là ít nhất. In ra tổng đó.

Ví dụ:

NhậpIn ra
7 2
1 2
1 3
2 4
2 5
5 6
3 7
4 6 7
4 5 6
6
3

Kiểm tra: nhập

10 3
1 2
2 3
3 4
2 5
5 6
1 7
7 8
7 9
9 10
4 6 10
8 10 3
1 1 1
Xem đáp số
8
6
0

Hướng dẫn giải chi tiết: liên hệ tác giả.

Cây có n đỉnh, gốc là đỉnh 1. Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), n - 1 dòng sau là các cạnh, q dòng sau mỗi dòng có u, k (0 ≤ k ≤ n). Với mỗi câu hỏi, in ra đỉnh đến được khi đi từ u lên phía gốc đúng k bước, hoặc -1 nếu đi quá gốc.

Ví dụ:

NhậpIn ra
7 4
1 2
1 3
2 4
2 5
5 6
3 7
6 2
6 4
7 0
4 2
2
-1
7
1

Kiểm tra: nhập

10 5
1 2
2 3
3 4
2 5
5 6
1 7
7 8
7 9
9 10
4 3
10 2
10 4
6 1
1 1
Xem đáp số
1
7
-1
5
-1

Hướng dẫn giải chi tiết: liên hệ tác giả.

Cây có n đỉnh. Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), n - 1 dòng sau là các cạnh, q dòng sau mỗi dòng có u, v, k. Với mỗi câu hỏi, in ra đỉnh thứ k trên đường đi từ u tới v (đỉnh u là thứ 0), hoặc -1 nếu đường đi ngắn hơn k cạnh.

Ví dụ:

NhậpIn ra
7 3
1 2
1 3
2 4
2 5
5 6
3 7
4 7 2
4 7 3
6 4 1
1
3
5

Kiểm tra: nhập

10 4
1 2
2 3
3 4
2 5
5 6
1 7
7 8
7 9
9 10
4 10 3
4 10 6
10 4 5
6 6 0
Xem đáp số
1
10
3
6

Hướng dẫn giải chi tiết: liên hệ tác giả.

Cây có n đỉnh, mỗi cạnh có trọng số. Dòng đầu có n, q (2 ≤ n ≤ 2 × 105, 1 ≤ q ≤ 2 × 105), n - 1 dòng sau mỗi dòng có u, v, w (1 ≤ w ≤ 109), q dòng sau mỗi dòng có u, v (u khác v). Với mỗi câu hỏi, in ra trọng số lớn nhất của một cạnh trên đường đi giữa u và v.

Ví dụ:

NhậpIn ra
5 3
1 2 3
1 3 4
3 4 2
3 5 6
2 4
4 5
5 1
4
6
6

Kiểm tra: nhập

8 4
1 2 5
2 3 1
2 4 7
1 5 2
5 6 9
6 7 3
5 8 8
3 7
4 8
6 5
3 4
Xem đáp số
9
8
9
7

Hướng dẫn giải chi tiết: liên hệ tác giả.

Cây có n đỉnh. Có m chuyến đi, chuyến thứ i đi từ u tới v theo đường duy nhất trên cây. Dòng đầu có n, m (1 ≤ n, m ≤ 2 × 105), n - 1 dòng sau là các cạnh, m dòng sau mỗi dòng có u, v. In ra số chuyến đi qua mỗi đỉnh 1, 2, …, n (kể cả đỉnh đầu, đỉnh cuối).

Ví dụ:

NhậpIn ra
7 3
1 2
1 3
2 4
2 5
5 6
3 7
4 6
6 7
3 3
1 2 2 1 2 2 1

Kiểm tra: nhập

10 4
1 2
2 3
3 4
2 5
5 6
1 7
7 8
7 9
9 10
4 6
10 8
4 10
6 6
Xem đáp số

1 2 2 2 1 2 2 1 2 2

Hướng dẫn giải chi tiết: liên hệ tác giả.

Như bài 9 nhưng đếm theo cạnh: in ra số chuyến đi qua từng cạnh, theo đúng thứ tự các cạnh trong dữ liệu vào. Dòng đầu có n, m (2 ≤ n ≤ 2 × 105, 1 ≤ m ≤ 2 × 105), n - 1 dòng sau là các cạnh, m dòng sau mỗi dòng có u, v.

Ví dụ:

NhậpIn ra
7 3
1 2
1 3
2 4
2 5
5 6
3 7
4 6
6 7
3 3
1 1 1 2 2 1

Kiểm tra: nhập

10 4
1 2
2 3
3 4
2 5
5 6
1 7
7 8
7 9
9 10
4 6
10 8
4 10
6 6
Xem đáp số

1 2 2 1 1 1 1 2 2

Hướng dẫn giải chi tiết: liên hệ tác giả.

Cây có n đỉnh. Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), n - 1 dòng sau là các cạnh, q dòng sau mỗi dòng có a, b, c, d. Với mỗi câu hỏi, in ra YES nếu đường đi a - b và đường đi c - d có ít nhất một đỉnh chung, ngược lại NO.

Ví dụ:

NhậpIn ra
7 3
1 2
1 3
2 4
2 5
5 6
3 7
4 6 1 7
4 5 6 3
4 6 2 2
NO
YES
YES

Kiểm tra: nhập

10 4
1 2
2 3
3 4
2 5
5 6
1 7
7 8
7 9
9 10
4 6 8 10
3 6 5 7
4 10 9 9
6 6 6 6
Xem đáp số
NO
YES
YES
YES

Hướng dẫn giải chi tiết: liên hệ tác giả.

Cây có n đỉnh, gốc là đỉnh 1. Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), n - 1 dòng sau là các cạnh, q dòng sau mỗi dòng bắt đầu bằng k rồi k đỉnh (tổng các k không quá 2 × 105). Với mỗi nhóm, in ra tổ tiên chung gần nhất của cả k đỉnh.

Ví dụ:

NhậpIn ra
7 3
1 2
1 3
2 4
2 5
5 6
3 7
2 4 6
3 4 6 7
1 5
2
1
5

Kiểm tra: nhập

10 4
1 2
2 3
3 4
2 5
5 6
1 7
7 8
7 9
9 10
3 4 6 3
2 8 10
4 4 6 10 2
1 9
Xem đáp số
2
7
1
9

Hướng dẫn giải chi tiết: liên hệ tác giả.