LOJ #6669,Nauuo and Binary Tree

神奇交互题。原题

题意大概就是,有一棵 \(n\) 个结点的 二叉树,我们并不知道这棵树的具体形态,只知道一个 \(n\) 和知道它的根是 \(1\)。每次可以询问树上两个点的距离,最后问你这棵树的形态,即输出每个点的父亲。

\(n \le 3000\) ,询问次数少于 \(30000\) 。

首先显然可以通过询问每个点到 \(1\) 的距离得到每个点的深度。那么感性一下,可以一层一层的处理,这样应该比较方便。

接下来是听课的时候听到的,被秀了一脸,就直接上做法再解释正确性了。完全不知道是怎么想到的。

给每个点维护一个 \(bot\) 值表示其所在重链的底部。\(n \le 3000\) ,这个可以直接每次大力更新。(维护 \(size,son\) )

令目前所询问的点为 \(\alpha\)

\(now\)表示当前点,然后每次从跟开始跳。跳的时候,询问 \(\alpha\) 跟 \(bot[now]\) 的距离,从而算出其 \(\text{lca}\) 深度。

那么从 \(now\) 开始跳重儿子直到来到 \(\text{lca}\) 处。此时,\(\alpha\) 一定在 \(now\) 的轻儿子那棵子树,跳过去就好。

循环直至此时 \(now\) 已经没有轻儿子。此时,由于我们是按深度逐层处理,\(\alpha\) 就是那棵轻儿子所在子树的根,也就是那个轻儿子。

连上边,一路上跳更新树链剖分情况。

注意到每次询问后要么结束,要么经过一条轻边。从任意一点到根所经过的轻边,不会超过 \(\log n\) 条。

也就是说,询问次数的数量级是 \(n \log n\) 的,并且完全跑不满。非常优秀的解决了。

代码:

#include <cstdio>
#include <vector>

const int maxn = 3e3 + 5;

inline int El_Psy_Congroo(int u,int v){
	printf("? %d %d\n",u,v);fflush(stdout);
	int dis ;scanf("%d",&dis) ;return dis ;
}

int n,dep[maxn],size[maxn],son[maxn],bot[maxn];
std::vector<int> Set[maxn];int answer[maxn];std::vector<int> e[maxn];

int until(int start,int goal){
	int now = start;
	while(dep[now] != goal) now = son[now];
	return now ;
}

inline int light_son(int now){
	if(e[now].size() <= 1) return 0;
	if(e[now].front() != son[now]) return e[now].front();return e[now].back();
}

void update(int fa,int now){
	answer[now] = fa;e[fa].push_back(now);
	size[now] = 1;bot[now] = now;
	if(son[fa] == 0) son[fa] = now,bot[fa] = now;
	while(fa){
		size[fa] ++;
		if(size[light_son(fa)]>size[son[fa]])
			son[fa] = light_son(fa);
		bot[fa] = bot[son[fa]],fa = answer[fa];
	}
}

int main(){
	scanf("%d",&n);dep[1] = 0 , size[1] = 1 , bot[1] = 1;
	for(int i=2;i<=n;i++) dep[i] = El_Psy_Congroo(1,i),Set[dep[i]].push_back(i);
	for(int i=1;i<=n;i++){
		if(Set[i].empty()) continue;
		for(int j=0;j<(int)Set[i].size();j++){
			int now = 1;
			while(true){
				int dis = El_Psy_Congroo(Set[i][j],bot[now]);
				int pos = (i+dep[bot[now]]-dis) >> 1;
				now = until(now,pos);
				if(light_son(now)) now = light_son(now);else break;
			}
			//dis(u,bot[now]) = i + dep[bot[now]] - 2*dep[lca(u,bot[now])]
			update(now,Set[i][j]);
		}
	}
	printf("! ");
	for(int i=2;i<=n;i++) printf("%d%c",answer[i]," \n"[i==n]);fflush(stdout);
	return 0 ;
}

posted @ 2021-02-08 11:49  RUI_R  阅读(138)  评论(0)    收藏  举报