题解:P7595 「EZEC-8」猜树
更好的阅读体验
Update 2025/10/04:修改了一处笔误。
我们考虑如何确定一个点
- 如果我们已经确定了
v 的深度为u 的深度+1 ,那么我们只需要检查u, v 的距离是否为1 。如果距离为1 就说明有边相连。 - 如果我们已经确定了
v 在u 的子树中,那么只需要检查v 的深度是否为u 的深度+1 。
后文中“查询次数”为输入的数字个数。假设树上深度为
我们考虑根号分治。设置阈值
所以查询次数为
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define N 2006
using namespace std;
int n,B,dep[N],fa[N];
vector<int> p[N];
int query1(int u,int v)
{
printf("? 1 %lld %lld\n",u,v),fflush(stdout);
return scanf("%lld",&u),u;
}
vector<int> query2(int u)
{
int sz;vector<int> vec;
printf("? 2 %lld\n",u),fflush(stdout);
scanf("%lld",&sz),vec.resize(sz);
for(int i=0;i<sz;i++)scanf("%lld",&vec[i]);
return vec;
}
main()
{
scanf("%lld",&n);
B=pow(n,0.5),p[dep[1]=1].push_back(1);
for(int i=2;i<=n;i++)
dep[i]=query1(1,i)+1,p[dep[i]].push_back(i);
for(int i=1;i<=n;i++)if(p[i].size())
{
if(p[i].size()<B)
{
for(int u:p[i])for(int v:p[i+1])
if(query1(u,v)==1)fa[v]=u;
} else {
for(int u:p[i])
{
vector<int> t=query2(u);
for(int v:t)if(dep[v]==dep[u]+1)fa[v]=u;
}
}
}
putchar('!');
for(int i=2;i<=n;i++)printf(" %lld",fa[i]);
putchar(10),fflush(stdout);
return 0;
}