题解:P14227 [ICPC 2024 Kunming I] 排列
题目分析
这是一道对思维、码力均有一定要求的题。不难想到
考虑合并询问。注意到二分的区间要么相等,要么无交,可以两数同时二分定位。如果区间相等,就猜测两数一左一右;如果区间无交,同样可以猜测两数一左一右。有
询问次数中的
AC Code
代码实现很复杂,省略一些几乎复制粘贴的片段。本地实测询问次数在
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,res,a[N],b[N],c[N];
random_device rd;
mt19937 gen(rd());
void query(){
cout<<'0';
for(int i=1;i<=n;i++)cout<<' '<<b[i];
cout<<endl;
cin>>res;
}
int main(){
cin>>n;
if(n==1){
cout<<"1 1";
return 0;
}
for(int i=1;i<=n;i++)a[i]=i;
for(;;){
shuffle(a+1,a+n+1,gen);
for(int i=1;i<=n>>1;i++)b[i]=a[1];
for(int i=(n>>1)+1;i<=n;i++)b[i]=a[2];
query();
if(res==1)continue;
if(!res)swap(a[1],a[2]);
int l1=1,r1=n>>1,l2=(n>>1)+1,r2=n;
while(l1<r1&&l2<r2){
int mid1=l1+r1>>1,mid2=l2+r2>>1;
for(int i=1;i<=n>>1;i++)b[i]=a[2];
for(int i=(n>>1)+1;i<=n;i++)b[i]=a[1];
for(int i=l1;i<=mid1;i++)b[i]=a[1];
for(int i=l2;i<=mid2;i++)b[i]=a[2];
query();
if(res==2){
r1=mid1,r2=mid2;
continue;
}
if(!res){
l1=mid1+1,l2=mid2+1;
continue;
}
for(int i=l2;i<=mid2;i++)b[i]=a[1];
query();
if(res)r1=mid1,l2=mid2+1;
else l1=mid1+1,r2=mid2;
}
while(l1<r1){
/*省略*/
}
while(l2<r2){
/*省略*/
}
c[l1]=a[1],c[l2]=a[2];
break;
}
if(n==2){
cout<<"1 "<<c[1]<<' '<<c[2]<<'\n';
return 0;
}
for(int o=3;o<n-1;o+=2){
int l1=1,r1=n-o+1,l2=1,r2=n-o+1;
while(l1<r1&&l2<r2){
int mid1=l1+r1>>1,mid2=l2+r2>>1;
fill(b+1,b+n+1,a[1]);
for(int i=1,j=1;i<=n;i++){
if(c[i])continue;
if(j>=l1&&j<=mid1)b[i]=a[o];
if(j>mid2&&j<=r2)b[i]=a[o+1];
j++;
}
query();
if(--res==2){
r1=mid1,l2=mid2+1;
continue;
}
if(!res){
l1=mid1+1,r2=mid2;
continue;
}
/*省略*/
}
while(l1<r1){
/*省略*/
}
while(l2<r2){
/*省略*/
}
/*省略*/
}
if(~n&1){
/*省略*/
}
int i=1;
while(c[i])i++;
c[i]=a[n];
cout<<'1';
for(int i=1;i<=n;i++)cout<<' '<<c[i];
}