一个 1 − n 1-n 1−n的排列 p p p,你可以询问至多 ⌊ 3 n 2 ⌋ + 30 \lfloor \frac{3n}{2}\rfloor+30 ⌊23n⌋+30次来得到这个排列
每次询问先选定一个数 x x x,然后询问分为两种
Ⅰ.得到 max { min ( x , p i ) , min ( x + 1 , p j ) } \max\{\min(x,p_i),\min(x+1,p_{j})\} max{min(x,pi),min(x+1,pj)}的值
Ⅱ.得到 min { max ( x , p i ) , max ( x + 1 , p j ) } \min\{\max(x,p_i),\max(x+1,p_{j})\} min{max(x,pi),max(x+1,pj)}的值
考虑取 x = 1 x=1 x=1代入,那么询问二大概会返回 min { p i , max ( 2 , p j ) } \min\{ p_i,\max(2,p_j)\} min{pi,max(2,pj)}
那么我们这样询问 ⌈ n 2 ⌉ \lceil \frac{n}{2}\rceil ⌈2n⌉次,第 k k k次询问 p 2 k − 1 p_{2k-1} p2k−1和 p 2 k p_{2k} p2k(如果 n n n为奇数最后一次询问 p n p_n pn和 p 1 p_1 p1)
若在这 ⌈ n 2 ⌉ \lceil \frac{n}{2}\rceil ⌈2n⌉次询问中某次返回的值为 1 1 1,可以断定此时 p i = 1 p_i=1 pi=1
如果任意一次询问的结果都不为 1 1 1,可以断定 1 1 1作为 p j p_j pj被访问到,在被访问到的那次询问中会返回 min { p i , 2 } = 2 \min\{ p_i,2\}=2 min{pi,2}=2
但是可能出现多次( 2 2 2次,值为 2 2 2的那个节点也可能返回 2 2 2)询问的返回值为 2 2 2,我们可以把这些询问问到的节点存起来,这些节点是可能为 1 1 1的节点
遍历每个可能节点 z z z,去询问 min { p z , max ( 2 , p j ) } \min\{ p_z,\max(2,p_j)\} min{pz,max(2,pj)}满足 ( j ! = z ) (j!=z) (j!=z)
若 p z = 1 p_z=1 pz=1可以确定返回值一定为 1 1 1,所以这样我们一定可以确定 1 1 1的位置
这样,我们使用不多于 ⌈ n 2 ⌉ + 4 \lceil \frac{n}{2} \rceil+4 ⌈2n⌉+4次询问确定了值为 1 1 1的节点是 k k k
这样我们遍历 [ 1 , n ] [1,n] [1,n],依次询问 max { min ( x , p i ) , min ( x + 1 , p j ) } \max\{\min(x,p_i),\min(x+1,p_{j})\} max{min(x,pi),min(x+1,pj)},我们取 x = n − 1 , i = k x=n-1,i=k x=n−1,i=k
那么返回值为 max { 1 , min ( n , p j ) } = p j \max\{1,\min(n,p_{j})\}=p_j max{1,min(n,pj)}=pj,直接就得到 p j p_j pj的值
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int n,a[maxn];
vector<int>possible;
int ask1(int id1,int id2,int x)
{
cout << "? " << 1 << " " << id1 << " " << id2 << " " << x << endl;
int val; cin >> val;
return val;
}
int ask2(int id1,int id2,int x)
{
cout << "? " << 2 << " " << id1 << " " << id2 << " " << x << endl;
int val; cin >> val;
return val;
}
int main()
{
ios::sync_with_stdio( false );
int t; cin >> t;
while( t-- )
{
possible.clear();
cin >> n;
int one = 0;
for(int i=1;i<=n;i+=2)
{
int id1 = i, id2 = i+1;
if( id2>n ) id2 = 1;
int x = ask2(id1,id2,1);
if( x==1 ) one = id1;
if( x==2 ) possible.push_back( id1 ), possible.push_back( id2 );
}
if( one==0 )//没有找到
{
for(auto v:possible )
for(int i=1;i<=n;i++)
{
if( i==v ) continue;
int x = ask2(v,i,1);
if( x==1 ) one = v;
break;
}
}
a[one] = 1;
for(int i=1;i<=n;i++)
{
if( one==i ) continue;
a[i] = ask1(one,i,n-1);
}
cout << "!";
for(int i=1;i<=n;i++) cout << " " << a[i];
cout << endl;
}
}