Subtask 1
注意到只有四个数。
可以先通过一次操作二找出 (3,4) 的位置和 (1,2) 的位置。
然后再分别用四次操作一,让 (3,4) 来对 (1,2) 取模确定四个位置。
n=read();m1=read();m2=read();m3=read();
if(n==4){
printf("? 4 1 2 3 4 3
");fflush(stdout);
k=read();a=read();b=read();
for(int i=1;i<=4;i++)if(i!=a&&i!=b){if(!c)c=i;else d=i;}
printf("! %d %d
",a,c);fflush(stdout);ans1=read();
printf("! %d %d
",a,d);fflush(stdout);ans2=read();
if(ans1+ans2==0) ed[b]=3,ed[a]=4;
else{
ed[b]=4,ed[a]=3;
if(ans1) ed[c]=2,ed[d]=1;else ed[d]=2,ed[c]=1;
printf("A %d %d %d %d
",ed[1],ed[2],ed[3],ed[4]);
fflush(stdout);return 0;
}
printf("! %d %d
",b,c);fflush(stdout);ans3=read();
printf("! %d %d
",b,d);fflush(stdout);ans4=read();
if(ans3) ed[c]=2,ed[d]=1;else ed[c]=1,ed[d]=2;
printf("A %d %d %d %d
",ed[1],ed[2],ed[3],ed[4]);
fflush(stdout);return 0;
}
Subtask 2
发现 (m_2=n),且 (m_3) 是 (m_2^2)。
那么我们可以通过 (n) 次询问来确定每个数的位置。
每次询问 (n) 个数,询问的上界减一,然后找出给的数中没有被确定位置的数即可。
n=read();m1=read();m2=read();m3=read();
if(n==500){
for(int i=n;i>=1;i--){
printf("? %d ",i);
for(int j=1;j<=n;j++)
if(!flag[j]) printf("%d ",j);
printf("%d
",i);fflush(stdout);
int x=read(),y=read();ed[y]=i,flag[y]=1;
}
printf("A ");
for(int i=1;i<=n;i++) printf("%d ",ed[i]);
fflush(stdout);return 0;
}
Subtask 3~6
没看出来性质 A 可以怎么搞。
看最下面的数据范围,发现 (m_1) 很大,(m_2) 很小,但是 (2^{17}>5 imes 10^4)。
这启发我们可以用类似倍增的做法来搞。
可以发现,((lceilfrac{n}{2} ceil,n]) 中的每个数对 (lceilfrac{n}{2} ceil) 取模都可以得到不同的结果。
所以,如果我们知道了 ([1,lceilfrac{n}{2} ceil]) 中每个数的位置,那么剩下的数位置可以通过不断进行操作一来确定。
想要确定 ([1,lceilfrac{n}{2} ceil]),可以转化为知道了 ([1,lceilfrac{n}{4} ceil]) 来用同样的方法求 ((lceilfrac{n}{4} ceil,lceilfrac{n}{2} ceil]) 得到。
所以我们可以通过递归分治的方式来处理。
我们在数轴上找出每个长为 (n imes frac{1}{2}^{i}) 的段,然后处理这一段内的数的位置即可。
void work(int l,int r,int cnt){
int mid=(r+1)/2+1;all=0;
if(l==r){for(int i=1;i<=n;i++)if(!pos[i]) pos[i]=cnt;return;}
for(int i=1;i<=n;i++)if(!pos[i]) que[++all]=i;
printf("? %d ",all);for(int i=1;i<=all;i++) printf("%d ",que[i]);
printf("%d
",mid);fflush(stdout);int x=read();
for(int i=1;i<=x;i++) pos[read()]=cnt;work(l,mid-1,cnt+1);
}
//下面是主函数内的部分
n=read();m1=read();m2=read();m3=read();
if(m2>=17){
for(int i=1;i<=n;i++) num[i]=i;
work(1,n,1);sort(num+1,num+n+1,cmp);
ed[num[1]]=1;ed[num[2]]=2;int maxx,id=num[2];
for(int i=3;i<=n;i++){
if(pos[num[i]]!=pos[num[i-1]]) lst=id,maxx=id=0;
printf("! %d %d
",num[i],lst);fflush(stdout);int x;
ed[num[i]]=ed[lst]+(x=read());if(!x)ed[num[i]]=(ed[lst]<<1);
if(maxx<ed[num[i]])maxx=ed[num[i]],id=num[i];
}
printf("A");for(int i=1;i<=n;i++) printf(" %d",ed[i]);
fflush(stdout);return 0;
}
Subtask 7
可以发现对于最后一组数据,(2^{15}le 5 imes 10^4<2^{16})。也就是说我们操作二理论上多进行了一次。
想办法怎么减少次数。
我们发现递归的时候是最后剩余了一个 (1) 然后退出的。此时我们可以找出 (1,2,3,4) 的位置。
如果我们不用操作二找出最后一个 (1) 的位置,而是采用 Subtask 1 的方法来的话,理论上就可以使操作二控制在一个可行的范围内了。
然后我们在 Subtask 3~6 的代码上套一个 Subtask 1 的代码就可以通过这个测试点。
void work2(int l,int r,int cnt){
int mid=(r+1)/2+1;all=0;
if(mid==2){for(int i=1;i<=n;i++)if(!pos[i]) pos[i]=cnt;return;}
for(int i=1;i<=n;i++)if(!pos[i]) que[++all]=i;
printf("? %d ",all);for(int i=1;i<=all;i++) printf("%d ",que[i]);
printf("%d
",mid);fflush(stdout);int x=read();
for(int i=1;i<=x;i++) pos[read()]=cnt;work2(l,mid-1,cnt+1);
}
//下面是主函数内的部分
for(int i=1;i<=n;i++) num[i]=i;
work2(1,n,1);sort(num+1,num+n+1,cmp);
int c=num[2],d=num[1],a=num[3],b=num[4];
printf("! %d %d
",a,c);fflush(stdout);ans1=read();
printf("! %d %d
",a,d);fflush(stdout);ans2=read();
if(ans1+ans2!=0){
ed[a]=3;ed[b]=4;
if(ans1) ed[c]=2,ed[d]=1;else ed[d]=2,ed[c]=1;
}
else{
ed[a]=4;ed[b]=3;
printf("! %d %d
",b,c);fflush(stdout);ans3=read();
printf("! %d %d
",b,d);fflush(stdout);ans4=read();
if(ans3) ed[c]=2,ed[d]=1;else ed[c]=1,ed[d]=2;
}
int maxx,id;
if(ed[num[3]]==4) id=num[3];
else id=num[4];
for(int i=5;i<=n;i++){
if(pos[num[i]]!=pos[num[i-1]]) lst=id,maxx=id=0;
printf("! %d %d
",num[i],lst);fflush(stdout);int x;
ed[num[i]]=ed[lst]+(x=read());if(!x)ed[num[i]]=(ed[lst]<<1);
if(maxx<ed[num[i]])maxx=ed[num[i]],id=num[i];
}
printf("A");for(int i=1;i<=n;i++) printf(" %d",ed[i]);
fflush(stdout);return 0;
但是这份代码又不能过掉 Subtask 3~4,因为在前面的小数据递归时,按 (n imes frac{1}{2}^i) 大小分的块可能使得 (3,4) 不在同一块中而 (4,5) 在同一块中,所以找完 (1) 到 (4) 的位置后再处理后面时 (5) 不会被处理到。
这里特判一下 (3,4) 是不是在一块就可以了。
但是我是个懒逼,所以我把上面所有的代码全拼起来了。
然后我就过了。