結果
| 問題 |
No.3237 Find the Treasure!
|
| コンテスト | |
| ユーザー |
👑 |
| 提出日時 | 2025-08-15 23:00:39 |
| 言語 | PyPy3 (7.3.15) |
| 結果 |
AC
|
| 実行時間 | 439 ms / 3,000 ms |
| コード長 | 4,682 bytes |
| コンパイル時間 | 280 ms |
| コンパイル使用メモリ | 82,084 KB |
| 実行使用メモリ | 100,112 KB |
| 平均クエリ数 | 13.83 |
| 最終ジャッジ日時 | 2025-08-15 23:01:00 |
| 合計ジャッジ時間 | 11,519 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge2 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 1 |
| other | AC * 22 |
ソースコード
import math
class LowestCommonAncestor:
"""
Lowest Common Ancestor (LCA)
---
木に対する最小共通祖先を求めるデータ構造
"""
def __init__(self, n: int):
"""\
木の頂点数 n を指定して初期化する
Parameters:
n (int): 木の頂点数
"""
self._n = n
self._logn = int(math.log2(self._n) + 2)
self._depth = [0] * self._n
self._distance = [0] * self._n
self._ancestor = [[-1] * self._n for _ in range(self._logn)]
self._edges = [[] for _ in range(self._n)]
def add_edge(self, u: int, v: int, w: int = 1):
"""\
u, v 間に重み w の辺を追加する
Parameters:
u (int): 辺の片方の頂点
v (int): 辺のもう片方の頂点
w (int): 辺の重み
"""
self._edges[u].append((v, w))
self._edges[v].append((u, w))
def build(self, root: int = 0):
"""\
根を root にした木を構築する
Parameters:
root (int): 根の頂点番号
"""
stack = [root]
while stack:
now = stack.pop()
for to, w in self._edges[now]:
if self._ancestor[0][to] == now or self._ancestor[0][now] == to:
continue
self._ancestor[0][to] = now
self._depth[to] = self._depth[now] + 1
self._distance[to] = self._distance[now] + w
stack.append(to)
for k in range(1, self._logn):
for i in range(self._n):
if self._ancestor[k - 1][i] == -1:
self._ancestor[k][i] = -1
else:
self._ancestor[k][i] = self._ancestor[k - 1][self._ancestor[k - 1][i]]
def lca(self, u: int, v: int) -> int:
"""\
u, v の最小共通祖先を求める
Parameters:
u (int): 頂点 u
v (int): 頂点 v
Returns:
lca (int): u, v の最小共通祖先
"""
# u の深さを v の深さ以下になるよう調整する
if self._depth[u] > self._depth[v]:
u, v = v, u
# v の深さを u に合わせる
for k in range(self._logn - 1, -1, -1):
if ((self._depth[v] - self._depth[u]) >> k) & 1 == 1:
v = self._ancestor[k][v]
# この時点で一致すれば、それが解
if u == v:
return u
# u, v がギリギリ一致しないよう親方向に辿る
for k in range(self._logn - 1, -1, -1):
if self._ancestor[k][u] != self._ancestor[k][v]:
u = self._ancestor[k][u]
v = self._ancestor[k][v]
# 最後に 1 ステップ親方向に辿った頂点が解
return self._ancestor[0][u]
# u, v (0-indexed) の距離を求める
def distance(self, u: int, v: int) -> int:
"""\
u, v 間の距離を求める
Parameters:
u (int): 頂点 u
v (int): 頂点 v
Returns:
dist (int): u, v 間の最短距離の長さ
"""
return self._distance[u] + self._distance[v] - 2 * self._distance[self.lca(u, v)]
# v の親を求める
def parent(self, v: int) -> int:
"""\
v の親を求める
Parameters:
v (int): 頂点 v
Returns:
parent (int): 頂点 v の親
"""
return self._ancestor[0][v]
N = int(input())
lca = LowestCommonAncestor(N)
edges = []
for _ in range(N - 1):
u, v = map(lambda x: int(x) - 1, input().split())
lca.add_edge(u, v)
edges.append((u, v))
lca.build()
even = {i for i in range(N) if lca._depth[i] % 2 == 0}
first_query = []
for u, v in edges:
if u in even:
first_query.append(u + 1)
else:
first_query.append(v + 1)
print("?", *first_query)
if input() == "Yes":
candidates = even
not_candidates = set(range(N)) - even
else:
candidates = set(range(N)) - even
not_candidates = even
while len(candidates) > 1:
new_candidates = set(list(candidates)[: len(candidates) // 2])
query = []
for u, v in edges:
if u in new_candidates:
query.append(u + 1)
elif v in new_candidates:
query.append(v + 1)
elif u in not_candidates:
query.append(u + 1)
else:
query.append(v + 1)
print("?", *query)
if input() == "Yes":
candidates = new_candidates
else:
candidates = candidates - new_candidates
print("!", candidates.pop() + 1)