比较显然的思路是找0的位置。
通过0来确定每个位置数的值。
如何找0的位置呢?
假如有a,b,c三个数
如果 ( a | b ) > ( b | c ) 则a一定不是0,显然前者b通过或a 结果变大了
如果 ( a | b ) < ( b | c ) 则c一定不是0,
如果 ( a | b ) == ( b | c ) 则b一定不是0,因为如果b是0,则有a==c,显然不可能。
通过上述我们就可以枚举,然后每次更新a,b 为0的候选位置。
需要的次数为 n+x
然后再枚举i,询问(a | i) (b | i)的关系,判断a,b谁是0.
需要的次数为y
最后枚举i,得出结果
需要的次数为n
总次数为2*n+x+y
先分析x,肯定是( a | b ) == ( b | c ) 时,我们需要额外询问一次 a | c 。
两个数或等于另外两个数 ,总共就11位数,数组随机的情况下,次数很少。
y同理。
通过随机数据可知 x+y约等于11 所以算法可行。。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 3000+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
int p[M];
int mp[M][M];
int ask(int x,int y)
{
if(x<y)swap(x,y);
if(mp[x][y])return mp[x][y];
cout<<"? "<<x<<" "<<y<<endl;
int tp;
cin>>tp;
mp[x][y]=tp;
return tp;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
int a=1,b=2,tp=0;
tp=ask(a,b);
for(int i=3;i<=n;i++)
{
int nw=ask(b,i);
if(tp>nw)//a一定不是0
{
a=i;
tp=nw;
}
else if(tp<nw);//c一定不是0
else //b一定不是0
{
tp=ask(a,i);
b=i;
}
}
// cout<<a<<" -- "<<b<< " -- "<<tp<<endl;
int id=0;
//while(1)int i=uniform_int_distribution<int>(1,n)(rng);
for(int i=1;i<=n;i++)
{
if(i==a||i==b)continue;
int x=ask(a,i),y=ask(b,i);
if(x==y)continue;
if(x<y)id=a;
else id=b;
break;
}
// cout<<id<<endl;
for(int i=1;i<=n;i++)
{
if(i==id)continue;
p[i]=ask(i,id);
}
cout<<"!";
for(int i=1;i<=n;i++)
cout<<" "<<p[i];
cout<<endl;
return 0;
}