1.题意
存在一个满 2 2 2 叉树,有 n n n 层(即有 2 n − 1 2 ^ n - 1 2n−1 个节点) 节点编号未知。
你需要在至多 4800 4800 4800 次询问中,找到每个节点的编号。
询问时,需要选择两个编号 u , v u,v u,v ($ 1 \leq u,v \leq 2 ^ n - 1$) 以 “? u , v u,v u,v” 的形式询问。
然后你会得到 u , v u,v u,v 编号所对于的节点的lca的编号 t t t 。
回答时,需要找到这颗树的结构 以 “! f 1 f 2 f 3 . . . f 2 n − 1 f_1\ f_2\ f_3 ...f_{2 ^ n - 1} f1 f2 f3...f2n−1” 输出。
其中 f i f_i fi 为 第 i i i 号节点的父亲节点的编号。
2.分析
关键在于 lca 的用处:剖链
钦定一个
u
u
u
剖出一条
u
u
u,根节点 与 一个叶子节点 的链。
若 $lca( u,v )\ \neq u $,见图
若 $lca( u,v )\ = u $,见图
此时将 u → v u \rightarrow v u→v 向更深处前进,直到 v ∈ l e a f n o d e v \in leafnode v∈leafnode
如果一条链确定了,整个链上节点的父子关系就确定了。
然后递归剖链,最后找到每个节点的父子关系。
询问次数最坏为大约 4600 4600 4600 次。
但是,链上节点先后顺序不清楚,如果再去用询问排序,询问次数达到了 6000 6000 6000 多次。
显然不可接受,
重新思考信息有没有能深度挖掘的地方。
我们来看一看满二叉树的形态
假设我们剖出的是最左侧的一条链,
不妨将这条链提起来
不难发现这条链上每个点以下的子树大小都不同。
子树大的层数小,子树小的层数大
其中,层数小的节点是层数大的节点的父亲。
只需记录下来就可以将链上节点排序。
至此问题解决。
3.code
my code:
#define useLL
#define interaction
#include <bits/stdc++.h>
using namespace std;
namespace AllRangeApply_Define{
#define CIN const int
#define il inline
#define l2(x) __lg(x)
#ifdef submit
#define MJY(p) freopen(p".in","r",stdin);freopen(p".out","w",stdout);
#define fileread(p) freopen(p".in","r",stdin);
#define filewrite(p) freopen(p".out","w",stdout);
#endif
#ifdef timecheck
#define endctime end = clock(); \
cerr<<"time = "<<double(end-start)/CLOCKS_PER_SEC<<"s"<<"\n";
#define endtime end = clock(); \
fprintf(stderr,"time = %.4lf s\n ",double(end-start)/CLOCKS_PER_SEC);
#define starttime clock_t start,end; \
start = clock();
#endif
#ifdef memcheck
#define mstart bool Med;
#define mend bool Mbe;
#define cmem fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0);
#endif
#ifdef useLL
#define int long long
#endif
// temp define is following
#ifdef interaction
#define ffclean fflush(stdout);
#endif
#ifdef useArr
#define arr(x) array<int,x>
#endif
}
using namespace AllRangeApply_Define;
namespace my{
#define pb insert
CIN N = 1030;
vector<int> nd[N];
pair<int,int> add(int k) {
pair<int,int> d;
d.first = nd[k].size();
d.second = k;
return d;
}
int n,f[N],ndct;
set<pair<int, int>> s[N];
void toAns() {
cout<<"! ";
for(int i = 1;i<(1<<n);i++) {
if(f[i])cout<<f[i]<<' ';
else cout<<"-1 ";
}
cout<<endl;
}
int ask(int u,int v) {
cout<<"? "<<u<<' '<<v<<endl;
int res;
cin>>res;
return res;
}
#define num second
void dfs(int from) {
int pos=*nd[from].begin();
s[from].pb(add(pos));
for(int ii = 1;ii<nd[from].size();ii++) {
int i = nd[from][ii];
int res = ask(pos,i);
if(res != pos && res != i) {
if(s[from].find({nd[res].size(),res}) != s[from].end()) s[from].erase({nd[res].size(),res});
nd[res].push_back(i);
s[from].pb(add(res));
}
if(res == pos){
s[from].pb(add(i));
pos = i;
}
}
f[s[from].rbegin()->num] = from;
auto it2 = s[from].rbegin();
for(auto it = s[from].rbegin();it != s[from].rend();it++) {
it2 = it;
it2++;
if(it2 == s[from].rend()) break;
f[it2->num] = it->num;
}
for(auto it = s[from].rbegin();it != s[from].rend();it++) if(!nd[it->num].empty()) dfs(it->num);
return;
}
signed main() {
cin>>n;
ndct = (1<<n) - 1;
for(int i = 1;i<=ndct;i++) nd[0].push_back(i);
dfs(0);
toAns();
return 0;
}
}
signed main() {
return my::main();
}