Link
首先我们进行(5)次查询:CC
、CH
、CO
、HO
、OO
。
这样我们就能够确定序列中((1,n))的所有字符。
同时所有O
的前一个字符和所有C
的后一个字符都已被确定。
对于位置(1),它有可能是O
或H
。
对于位置(n),它有可能是C
或H
。
也就是总共只有(4)种可能的情况了。
那么我们再进行(3)次长度为(n)的查询即可。
这样的总代价为(1.25+frac3{n^2}),当(nge5)时可以通过。
然后我们特判(n=4)的情况。
首先我们进行(4)次查询:CC
、CH
、CO
、HO
。
如果此时位置(2)和位置(3)已经被确定,而位置(1)和位置(4)未被确定.
注意到位置(1)不可能为C
,那么总共就只有(6)种可能的情况,再进行(5)次长度为(n)的查询即可,总代价为(1.3125)。
如果此时位置(2)和位置(3)未被确定,那么我们再查询OO
。
这样我们一定能够确定位置(2)和位置(3),并且它要么是OO
要么是HH
。
如果是OO
那么位置(1)也被确定了,位置(n)只有(2)种可能的字符,再进行(1)次长度为(n)的查询即可,总代价为(1.3125)。
如果是HH
那么我们查询一次HHH
,这样也能够确定所有的字符了。
#include<cstdio>
#include<cstring>
int read(){int x;scanf("%d",&x);return x;}
int n;char s[60],t[60],c[4]="OHC";
void ask()
{
printf("? %s
",t+1),fflush(stdout);
int m=read();
for(int i=1;i<=m;++i) for(int p=read()-1,j=1;t[j];++j) s[p+j]=t[j];
}
void work1(){for(int i=0;i<2;++i)for(int j=0;j<3;++j)memcpy(t,s,n+1),t[1]=c[i],t[4]=c[j],ask();}
void solve2()
{
for(int i=0;i<3;++i)
for(int j=0;j<3;++j)
{
if((i==2&&!s[1])||(j==0&&!s[n])||(s[1]&&s[1]^c[i])||(s[n]&&s[n]^c[j])||(!s[1]&&!s[n]&&i==1&&j==2))continue;
memcpy(t,s,n+1),t[1]=c[i],t[n]=c[j],ask();
}
if(!s[1]&&!s[n]) s[1]='H',s[n]='C';
}
int main()
{
for(int T=read();T;--T)
{
n=read(),memset(s,0,sizeof s),memset(t,0,sizeof t);
t[1]='C',t[2]='C',ask(),t[1]='C',t[2]='H',ask(),t[1]='C',t[2]='O',ask(),t[1]='H',t[2]='O',ask();
if(n==4&&!s[1]&&s[2]&&s[3]&&!s[4]) work1();
else
{
t[1]='O',t[2]='O',ask();
for(int i=2;i<n;++i) if(!s[i]) s[i]='H';
if(n==4&&!s[1]&&s[2]=='H'&&s[3]=='H'&&!s[4])
{
t[1]='H',t[2]='H',t[3]='H',ask();
if(!s[1]) s[1]='O';
if(!s[4]) s[4]='C';
}
else solve2();
}
printf("! %s
",s+1),fflush(stdout),read();
}
}