CodeForces 1174F Ehab and the Big Finale
思路
看到询问次数是 \(O(\log n)\) 级别的,考虑使用树剖的一些性质。
我们都知道一个点到根结点的链经过的轻边为 \(O(\log n)\) 级别的。于是考虑如下的算法:
- 先通过一次询问得出 \(x\) 的深度,然后树剖。
- 一开始设 \(u \to 1\)。沿着重链跳到和 \(x\) 深度相同的儿子,然后询问它们的 \(\mathrm{LCA}\)。\(u \to \mathrm{s}(\mathrm{LCA})\),重复操作直到得出 \(x\) 为止。
注意还有个细节。有可能重链底的点的深度比 \(x\) 小,所以树剖时要特判。
总询问次数为 \(2 \log n + 1\),可以通过。
代码
code
/*
p_b_p_b txdy
AThousandSuns txdy
Wu_Ren txdy
Appleblue17 txdy
*/
#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 200100;
int n, head[maxn], len, dx, b[maxn];
int fa[maxn], sz[maxn], son[maxn], dep[maxn], maxd[maxn];
struct edge {
int to, next;
} edges[maxn << 1];
void add_edge(int u, int v) {
edges[++len].to = v;
edges[len].next = head[u];
head[u] = len;
}
int ask1(int x) {
printf("d %d\n", x);
fflush(stdout);
scanf("%d", &x);
return x;
}
int ask2(int x) {
printf("s %d\n", x);
fflush(stdout);
scanf("%d", &x);
return x;
}
int dfs(int u, int f, int d) {
dep[u] = maxd[u] = d;
fa[u] = f;
sz[u] = 1;
int maxson = -1;
for (int i = head[u]; i; i = edges[i].next) {
int v = edges[i].to;
if (v == f) {
continue;
}
sz[u] += dfs(v, u, d + 1);
maxd[u] = max(maxd[u], maxd[v]);
if (sz[v] > maxson && maxd[v] >= dx) {
son[u] = v;
maxson = sz[v];
}
}
return sz[u];
}
void solve() {
scanf("%d", &n);
for (int i = 1, u, v; i < n; ++i) {
scanf("%d%d", &u, &v);
add_edge(u, v);
add_edge(v, u);
}
dx = ask1(1) + 1;
dfs(1, -1, 1);
int u = 1;
while (1) {
while (dep[u] < dx) {
b[dep[u]] = u;
u = son[u];
}
int dis = ask1(u);
if (!dis) {
printf("! %d\n", u);
fflush(stdout);
return;
}
int lca = b[dx - dis / 2];
if (dep[lca] + 1 == dx) {
printf("! %d\n", ask2(lca));
fflush(stdout);
return;
}
u = ask2(lca);
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}