[IOI2018]组合动作——构造
题目连接:
题目大意:有一个未知的长度为n的字符串$T$,只包含$A,B,X,Y$四个字符且首字母只出现一次,每一次你可以询问一个长度不超过$4n$的字符串$S$,交互库会返回$S$的子串与$T$的前缀的最大公共长度,要求在不超过$n+2$次询问后获得$T$串。
首先首字母只出现一次,我们可以先搞清首字母是什么。
如果一个一个试需要三次,但如果我们二分询问就只需要两次。
具体来说第一次询问$AB$,如果返回$1$则询问$A$,否则询问$X$。
得到首字母之后我们可以想到一个$O(2n)$的做法:
剩下每一位只有三种可能性,我们每次在已知前缀后面加一个字母来试出下一个,每一位需要试两次。
但是可以发现我们并没有用到询问串长度不大于$4n$的条件。
我们假设首字母为$A$,当前已知$T$串的前缀为$s$,那么利用上述条件我们可以用一次询问就得出下一个字母是什么。
因为首字母只会出现一次,所以我们可以将四个长度不超过$n$的询问串连在一起询问,而询问时匹配的$S$的子串一定是四个拼接的询问串中的一个,而不会跨越两个串。
那么我们只需要每次查询$sBsXBsXXsXY$即可,如果返回值为$0$则是$Y$;返回值是$1$则是$B$;返回值是$2$则是$X$。
但要注意最后一位这样询问会超出询问长度限制且返回值无法判断,所以需要特判,这样第一位和最后一位消耗两次,其他位的确定只消耗一次,一共$n+2$次。
#include"combo.h" #include<cstring> using namespace std; string first; string now; string s1,s2,s3; int cnt; int n; std::string guess_sequence(int N) { n=N; first=press("AB")?(press("A")?"A":"B"):(press("X")?"X":"Y"); now=first; first=="A"?s1="B",s2="X",s3="Y":"1"; first=="B"?s1="A",s2="X",s3="Y":"1"; first=="X"?s1="B",s2="A",s3="Y":"1"; first=="Y"?s1="B",s2="X",s3="A":"1"; if(n==1) { return now; } for(int i=2;i<n;i++) { int ans=press(now+s1+now+s2+s1+now+s2+s2+now+s2+s3)-(i-1); ans==0?now+=s3:(ans==1?now+=s1:now+=s2); } press(now+s1)==n?now+=s1:(press(now+s2)==n?now+=s2:now+=s3); return now; }