题解 CF1174F 【Ehab and the Big Finale】

从这个题来的 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;
}
原文地址:https://www.cnblogs.com/colazcy/p/13875490.html