从这个题来的 LOJ #6669. Nauuo and Binary Tree
Solution CF1174F Ehab and the Big Finale
题目大意:给定一棵节点数不超过 (2 imes10^5) 的树,其中隐藏一个节点 (x)。你可以询问一个点 (u) 到 (x) 的距离,或者询问点 (u) 到 (x) 路径上的第二个点((u) 必须为 (x) 的父亲)。在不超过 (36) 次询问内找出 (x)。
树链剖分、交互
分析:我们仍然考虑树链剖分(重链剖分)
做法与LOJ那题类似,初始链为 (1) 号点所在的链,如果 (x) 不在链上,我们就找出链底和 (x) 的 (LCA),询问 (LCA) 到 (x) 路径上的第二个点,跳到那个点(一定是轻儿子)所在的链上。重链剖分保证了询问次数是(O(logn))的
找 (LCA) 的话我们得一次询问完成,不然是过不去的。我一开始同时询问 (x) 到链顶和链底的距离来找 (LCA) ,这样每次跳一条链会询问三次,十分危险。
可以利用树上差分来完成,假设点 (u) 到根的距离为 (dis[u]),那么我们先问出 (dis[x]),链底 (t) 的 (dis) 我们已知,那么我们就可以求出 (LCA) 的 (dis)
(d(t,x)=dis[t]+dis[x]-2 imes dis[LCA])
这样每次跳一条链我们只需要问 (2) 次,可以通过
#include <cstdio>
#include <cctype>
#include <vector>
using namespace std;
const int maxn = 2e5 + 100;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
}
inline int query_dis(int u){
printf("d %d
",u);
fflush(stdout);
int res;
scanf("%d",&res);
return res;
}
inline int query_nxt(int u){
printf("s %d
",u);
fflush(stdout);
int res;
scanf("%d",&res);
return res;
}
vector<int> G[maxn],chain[maxn];
inline void addedge(int u,int v){G[u].push_back(v);}
int dfn[maxn],siz[maxn],top[maxn],son[maxn],dep[maxn],faz[maxn],dfs_tot;
inline void dfs1(int u){
dfn[u] = ++dfs_tot;
siz[u] = 1;
for(int v : G[u]){
if(v == faz[u])continue;
faz[v] = u;
dep[v] = dep[u] + 1;
dfs1(v);
siz[u] += siz[v];
if(siz[v] > siz[son[u]])son[u] = v;
}
}
inline void dfs2(int u,int tp = 1){
top[u] = tp;
chain[tp].push_back(u);
if(son[u])dfs2(son[u],tp);
for(int v : G[u]){
if(v == faz[u] || v == son[u])continue;
dfs2(v,v);
}
}
int n;
int main(){
n = read();
for(int u,v,i = 1;i < n;i++)
u = read(),v = read(),addedge(u,v),addedge(v,u);
dfs1(1);
dfs2(1);
int now = 1;
int dx = query_dis(1);
while(true){
int dis = query_dis(chain[top[now]].back());
if(dis == 0){
int ans = chain[top[now]].back();
printf("! %d
",ans);
fflush(stdout);
return 0;
}
int dl = (dep[chain[top[now]].back()] + dx - dis) >> 1;
int lca = chain[top[now]][dl - dep[chain[top[now]].front()]];
if(dep[chain[top[now]].back()] - dep[lca] == dis){
printf("! %d
",lca);
fflush(stdout);
return 0;
}
now = query_nxt(lca);
}
return 0;
}