给定一个[0,n-1]排列p,每次询问(i,j)返回pi|pj,最多4269次询问,推出这个排列
本题关键在于确定0的位置
一个结论:我们可以通过两次询问,从三个数中排除掉一个肯定不是0的数
因此:我们维护住两个值下标a,b,并且假设0在pa,pb这两个数中出现
初始时a=0,b=1,然后枚举c=[2..n-1]
pa|pc > pb|pc : 那么pa必然不是0,我们把a换成c
pa|pc < pb|pc:那么pb必然不是0,我们把b换成c
pa|pc = pb|pc:那么pc必然不是0,不用换
枚举完后我们可以确定0必定在pa,或pb两个数中出现,而要确定究竟在哪个位置,我们只需要随机选其他任何一个位置x,
如果pa|px != pb|px,就可以确定0的位置了,这个步骤重复个几次(题目的范围允许100+次)就可以
确定0的位置后,其他数询问起来就很简单了
ps:我的电脑好像不能写random了。。迷惑。。
搬运下题解,以后再写一次
#include <bits/stdc++.h> using namespace std; mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); int p[(1<<11)+5]; int query(int i,int j) { printf("? %d %d\n",i,j); fflush(stdout); int ans; scanf("%d",&ans); assert(ans!=-1); return ans; } int main() { int n; scanf("%d",&n); vector<int> qp; for (int i=1;i<=n;i++) qp.push_back(i); int a=qp[0],b=qp[1],val=query(a,b); for (int i=2;i<n;i++) { int tmp=query(b,qp[i]); if (tmp<val) { a=qp[i]; val=tmp; } else if (tmp==val) { b=qp[i]; val=query(a,qp[i]); } } int idx; while (1) { int i=uniform_int_distribution<int>(1,n)(rng); if (i==a || i==b) continue; int t1=query(i,a),t2=query(i,b); if (t1!=t2) { idx=(t1<t2? a:b); break; } } for (int i=1;i<=n;i++) { if (i!=idx) p[i]=query(i,idx); } printf("!"); for (int i=1;i<=n;i++) printf(" %d",p[i]); printf("\n"); fflush(stdout); }