1. 程式人生 > 實用技巧 >題解 CF1174F 【Ehab and the Big Finale】

題解 CF1174F 【Ehab and the Big Finale】

從這個題來的 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\)

不在鏈上,我們就找出鏈底和 \(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\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;
}