結果
問題 |
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)