Solution Nauuo and Binary Tree
题目大意:有一棵 (n) 个节点的二叉树,根节点为 (1),你可以通过询问两个点之间简单路径的距离来还原这棵树,(nleq 3000),询问次数上限 (30000)
树剖,交互
分析:首先询问出所有点的深度,一层一层扩展。
加入一个点后就暴力树剖一次,从 (1) 号点开始,每次询问当前点和重链底的距离,求出 (LCA) 的深度,如果没有找到父亲,就跳到 (LCA) 的轻儿子继续找。
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 3333;
int faz[maxn],dfn[maxn],top[maxn],siz[maxn],son[maxn],dep[maxn],dfs_tot,n;
vector<int> G[maxn],vec[maxn],chain[maxn];
inline void addedge(int u,int v){
G[u].push_back(v);
faz[v] = u;
}
inline void dfs1(int u){
dfn[u] = ++dfs_tot;
siz[u] = 1;
son[u] = 0;
for(int v : G[u]){
if(v == faz[u])continue;
dep[v] = dep[u] + 1;
faz[v] = u;
dfs1(v);
siz[u] += siz[v];
if(siz[v] > siz[son[u]])son[u] = v;
}
}
inline void dfs2(int u,int tp){
chain[tp].push_back(u);
top[u] = tp;
if(son[u])dfs2(son[u],tp);
for(int v : G[u]){
if(v == faz[u] || v == son[u])continue;
dfs2(v,v);
}
}
inline void build(){
dfs_tot = 0;
for(int i = 1;i <= n;i++)chain[i].clear();
dfs1(1);
dfs2(1,1);
}
inline int query(int u,int v){
printf("? %d %d
",u,v);
fflush(stdout);
int res = 0;
scanf("%d",&res);
return res;
}
inline void out(){
printf("! ");
for(int i = 2;i <= n;i++)printf("%d ",faz[i]);
printf("
");
fflush(stdout);
}
int main(){
dep[1] = 1;
scanf("%d",&n);
vec[1].push_back(1);
for(int i = 2;i <= n;i++)
vec[query(1,i) + 1].push_back(i);
for(int x : vec[2])
addedge(1,x);
build();
for(int i = 3;!vec[i].empty();i++)
for(int x : vec[i]){
int now = 1;
while(true){
int dis = query(chain[top[now]].back(),x);
if(dis == 1){
addedge(chain[top[now]].back(),x);
build();
break;
}
int dl = (dep[chain[top[now]].back()] + i - dis) >> 1;
int lca = chain[top[now]][dl - dep[chain[top[now]].front()]];
if(G[lca].size() < 2){
addedge(lca,x);
build();
break;
}
for(int v : G[lca])
if(v != son[lca]){
now = v;
break;
}
}
}
out();
return 0;
}