题目链接
题解
Orz yyf太神了,出这种又有意思又有意义的好题造福人类……
首先(n)次询问求出所有节点的深度。
考虑按深度扩展(BFS), 同时维护重链剖分
每次扩展一个点时,从根节点所在重链开始,每次询问当前节点与链底节点的距离,这样就可以算出它们LCA的深度,也就是当前节点到根的路径上与这条重链相交部分的最大深度。那么如果这个最大深度等于当前深度(-1), 就得到了该点的父亲;否则跳到LCA的轻儿子所在重链上,继续询问即可。
时间复杂度(O(n^2)), 询问次数(O(nlog n)).
代码
比我想象中的好写一些。
#include<bits/stdc++.h>
#define llong long long
using namespace std;
inline int read()
{
int x = 0,f = 1; char ch = getchar();
for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
return x*f;
}
const int N = 3000;
int son[N+3][2];
int hvs[N+3];
int dep[N+3];
int fa[N+3];
int sz[N+3];
vector<int> idep[N+3];
int n;
int getbtn(int u) {while(son[u][hvs[u]]) {u = son[u][hvs[u]];} return u;}
int jumpup(int u,int x) {while(x--) {u = fa[u];} return u;}
int jumpdown(int u,int x) {while(x--) {u = son[u][hvs[u]];} return u;}
void verify(int u) {hvs[u] = (sz[son[u][1]]>sz[son[u][0]]);}
void setfa(int u,int v) //fa[u]=v
{
// printf("setfa %d %d
",u,v);
fa[u] = v; sz[u] = 1; son[v][son[v][0]?1:0] = u;
while(v)
{
verify(v); sz[v]++; v = fa[v];
}
}
void work(int u)
{
int v = getbtn(1);
while(1)
{
printf("? %d %d
",v,u); fflush(stdout);
int dis; scanf("%d",&dis);
int deplca = (dep[u]+dep[v]-dis)>>1;
v = jumpup(v,dep[v]-deplca);
if(dep[u]-deplca==1) {setfa(u,v); return;}
v = son[v][hvs[v]^1]; v = getbtn(v);
}
}
int main()
{
scanf("%d",&n);
for(int i=2; i<=n; i++) {printf("? %d %d
",1,i); fflush(stdout); scanf("%d",&dep[i]); idep[dep[i]].push_back(i);}
for(int i=0; i<idep[1].size(); i++) {setfa(idep[1][i],1);}
for(int i=2; i<=n; i++)
{
for(int j=0; j<idep[i].size(); j++)
{
int u = idep[i][j];
work(u);
}
}
printf("! "); for(int i=2; i<=n; i++) printf("%d ",fa[i]); puts(""); fflush(stdout);
return 0;
}