CF1292E

link

题意:有一个包含O,H,C的串,你现在只知道长度,每次可以询问一个串t在这个串里的所有出现位置,代价是\(\frac{1}{|t|^2}\),你需要用不超过1.4的代价询问出这个串。

这个做法目前是\(1.333333\)的。

下面用1来代替C,2来代替H,3来代替O。

考虑可以先询问长度为2的串,可以得到每个字符前后可以接哪些字符。

假设我们已经有确定的位置了,可以进行如下操作来找到原串:

对于每个极长(已经确定位置的)串,如果前一个/后一个可以确定,就加上这个字符

如果没有字符能被这样加上,就找到最长的子串,在前面和后面中选出可能情况较少的一边,遍历每个取值,询问直到加上这个取值出现在原串里(最后一个取值不用询问)

那么现在考虑询问长度为2的串来找到确定的位置

考虑询问完一个串之后,除了出现位置其它位置都不是这个子串。

那么考虑先按照11,12,13的顺序询问,如果找到了11或12子串,可以直接暴力搞出原串。否则询问完13之后就没有1开头的串了,也就是除了最后一个位置,其它没确定的位置一定不是1,如果找到了13可以暴力判,次数<= \(0.75+\sum_{i=3}^n\frac{1}{i^2}\) 其中某一个串找到了位置,则可以直接暴力判。

如果都没找到可以考虑询问23和32,如果找到了至少一个,那么剩下除了最后一个位置都已经确定了,只用询问一次长度为n

如果全部没找到则串只有可能是22221 33331 22222 33333这四种情况,可以判一次2222再判一次33333解决,代价是\(1.25+\frac{1}{(n-1)^2}+\frac{1}{n^2}\),\(n=4\)时为\(1.423611\)\(n=5\)时为\(1.3525\)

考虑优化\(n=4\)的情况。

考虑询问23之后的局面

如果发现了23可以询问32再判一次是1.3125的

如果没发现则只能333...222..1这种串了

情况大概有这些:

3331
3321
3221
2221
3333
3332
3322
3222
2222

考虑按照长度为3的子串分组:

3331 (333)
3333
3332

 2221 (222)
3222
2222

3322
 3221 (322)

3321 (3321)
直接按顺序询问不够优秀,因为最后一组个数太多,可以和第二组交换

所以流程就变成了:

询问 333 如果存在就判断3331/3332 (1次3 2次4) 否则
询问 322 如果存在就得到答案 否则
询问 222 如果存在就得到答案 否则答案是3321
于是\(n=4\)最坏情况变成了1.333,已经足以通过了

考虑优化\(n>4\)的前面一部分:

注意到最坏情况依然是23和32都没找到的情况,考虑套用类似优化:

考虑询问23之后的局面

如果发现了23可以询问32再判一次

如果没发现则只能333...222..1这种串了

不过n增加之后不好找规律了,考虑通用操作

(下文无特殊情况均为len=n-1)

先判333...
然后333...2
然后333...22
然后类似的直到发现子串或者322...也没找到
如果发现子串那么一定最多只有最后一个字符不知道,可以是1/2,暴力询问
如果没发现那么222..一定是一个子串,判222..1或222..2。

这一部分最坏变成了1.29

然后我不会优化n=4了

贴个代码

int n;
int nx[4][4];
int w[55];
int od[4],id[4];
char s[]={" CHO"};
bool ask(const vector<int>&vt){
	cout<<"? ";
	for(auto i:vt)cout<<s[i];
	cout<<endl;
	int k;
	cin>>k;
	if(!~k)exit(0);
	if(!k)return 0;
	int m=vt.size();
	for(;k;--k){
		int x;
		cin>>x;
		for(int i=0;i<m;++i)w[i+x]=vt[i];
	}
	return 1;
} 
bool find(){
	int s=-1,at=0;
	for(int i=1;i<=n;++i)if(w[i]){
		if(i>1&&id[w[i]]==1){
			for(int j=1;j<=3;++j)if(nx[j][w[i]]){w[i-1]=j;return 1;}
		}
		int j=i;
		while(w[j+1])++j;
		if(j<n&&od[w[j]]==1){
			for(int k=1;k<=3;++k)if(nx[w[j]][k]){w[j+1]=k;return 1;}
		}
		if(chkmax(s,j-i))at=i;
		i=j;
	}
	if(s==n-1)return 0;
	int wi=at==1?inf:id[w[at]],wo=at+s==n?inf:od[w[at+s]]-(at+s==n-1||od[1]?0:1);
	if(wi<=wo){
		vector<int> q(s+2);
		for(int i=0;i<=s;++i)q[i+1]=w[i+at];
		int c=id[w[at]];
		for(int i=1;i<=3;++i)if(nx[i][w[at]]){
			if(c>1){
				q[0]=i;
				if(ask(q))return 1;
				--c;
			}
			else {w[at-1]=i;return 1;}
		}
	}
	else{
		vector<int> q(s+2);
		for(int i=0;i<=s;++i)q[i]=w[i+at];
		int c=wo;
		for(int i=1;i<=3;++i)if(nx[w[at+s]][i]&&(i!=1||at+s==n-1||od[1])){
			if(c>1){
				q[s+1]=i;
				if(ask(q))return 1;
				--c;
			}
			else {w[at+s+1]=i;return 1;}
		}
	}
	return 0;
}
bool try2(int x,int y){
	if(ask({x,y})){nx[x][y]=0;--od[x];--id[y];return 1;}
	else {nx[x][y]=0;--od[x];--id[y];return 0;}
}
void answer(){
	cout<<"! ";
	for(int i=1;i<=n;++i)cout<<s[w[i]];
	cout<<endl;
	int k;
	cin>>k;
	if(!~k)exit(0);
	for(int i=1;i<=n;++i)w[i]=0;
}
bool gg(){
	if(n==4){
		if(ask({3,3,3})){
			nx[3][3]=0;
			if(!w[4])return ((ask({3,3,3,1}))||(w[4]=2)),0;
			return 0;
		}
		else if(ask({2,2,2})){
			nx[2][2]=0;
			if(!w[4])w[4]=1;
			if(!w[1])w[1]=3;
			return 0;
		}
		else if(ask({3,2,2})){
			if(w[1])w[4]=1;
			else w[1]=3;
			return 0;
		}
		else return (w[1]=3,w[2]=3,w[3]=2,w[4]=1),0;
	}
	else{
		vector<int> v(n-1,3);
		int m=n-1;
		while(m&&!ask(v)){v[m-1]=2;--m;}
		for(int i=0;i<n-1;++i)w[i+1]=v[i];
		while(find());
		return 0;
	}
}
bool fnd(){
	while(find());
	return 1;
}
void query(){
	for(int i=1;i<=3;++i)for(int j=1;j<=3;++j)nx[i][j]=1;
	for(int i=1;i<=3;++i)od[i]=id[i]=3;
	(try2(1,1)||try2(1,2)||try2(1,3)||(try2(3,2)&&(try2(2,3)||1))||gg())&&fnd();
	answer();
}
signed main(){
	int t;
	cin>>t;
	for(;t;--t){
		cin>>n;
		query();
	}
	return 0;
}