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  阅读(101)  评论(0编辑  收藏  举报