題解 CF1174F 【Ehab and the Big Finale】
阿新 • • 發佈:2020-10-26
從這個題來的 LOJ #6669. Nauuo and Binary Tree
Solution CF1174F Ehab and the Big Finale
題目大意:給定一棵節點數不超過 \(2\times10^5\) 的樹,其中隱藏一個節點 \(x\)。你可以詢問一個點 \(u\) 到 \(x\) 的距離,或者詢問點 \(u\) 到 \(x\) 路徑上的第二個點(\(u\) 必須為 \(x\) 的父親)。在不超過 \(36\) 次詢問內找出 \(x\)。
樹鏈剖分、互動
分析:我們仍然考慮樹鏈剖分(重鏈剖分)
做法與LOJ那題類似,初始鏈為 \(1\) 號點所在的鏈,如果 \(x\)
找 \(LCA\) 的話我們得一次詢問完成,不然是過不去的。我一開始同時詢問 \(x\) 到鏈頂和鏈底的距離來找 \(LCA\) ,這樣每次跳一條鏈會詢問三次,十分危險。
可以利用樹上差分來完成,假設點 \(u\) 到根的距離為 \(dis[u]\),那麼我們先問出 \(dis[x]\),鏈底 \(t\) 的 \(dis\) 我們已知,那麼我們就可以求出 \(LCA\) 的 \(dis\)
\(d(t,x)=dis[t]+dis[x]-2\times 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\n",u); fflush(stdout); int res; scanf("%d",&res); return res; } inline int query_nxt(int u){ printf("s %d\n",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\n",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\n",lca); fflush(stdout); return 0; } now = query_nxt(lca); } return 0; }