[LOJ6669] Nauuo and Binary Tree 题解
绝对好题。
考虑每个点插入的次数必须为 \(\log n\) 级别的,而且还要再小。考虑重链剖分。当然,首先要询问出所有点的深度,并且按深度从小到大依次插入。
每次选择当前重链的链尾,若链尾深度为 \(dep\),询问返回值为 \(dp\),目标父亲深度为 \(d\),则在这条重链上深度为 \(d-\lfloor\frac{dp+d-dep}2\rfloor\) 的位置上的点,将会是这条重链上最后一个要插入点的祖先。然后从这个点转轻儿子即可,由于只有一个重儿子和一个轻儿子,所以时间复杂度就是原图中要插入的点到根节点路径上的重链的数量。数量级别远小于 \(\log n\),可以通过。
由于 \(n\le 3000\),所以可以每次插入后暴力重构每个点的重链信息。时间复杂度 \(O(n^2\log n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=3005;
int n,ch[N][2],dep[N];
int sz[N],fa[N],sn[N];
struct ad{int x,d;}a[N];
int cmp(ad x,ad y){
return x.d<y.d;
}int dfs(int x,int d){
if(dep[x]==d||!ch[x][sn[x]]) return x;
return dfs(ch[x][sn[x]],d);
}void add(int x,int y,int d){
if(dep[x]==d){
ch[x][ch[x][0]>0]=y;
return fa[y]=x,void();
}int z=dfs(x,d),dp;
cout<<"? "<<z<<" "<<y<<endl;
cin>>dp,dp=d-(dp+d-dep[z])/2;
int to=dfs(x,dp);
if(dp==d) add(to,y,d);
else add(ch[to][!sn[to]],y,d);
}void tree(int x){
sz[x]=1;if(!ch[x][0]) return;
for(int i=0;i<2;i++)
if(ch[x][i]) tree(ch[x][i]),sz[x]+=sz[ch[x][i]];
sn[x]=(sz[ch[x][0]]<sz[ch[x][1]]);
}int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=2;i<=n;i++){
cout<<"? 1 "<<i<<endl;
cin>>dep[i],a[i]={i,dep[i]};
}sort(a+2,a+n+1,cmp);
for(int i=2;i<=n;i++)
add(1,a[i].x,a[i].d-1),tree(1);
cout<<"! ";
for(int i=2;i<=n;i++)
cout<<fa[i]<<" ";
return cout<<endl,0;
}