vj传送门
本题实在是非常妙,首先二分求出
0
0
0和
1
1
1中数量较小的一个的数量,方便起见,假设
0
0
0数量最少,设
c
n
t
0
,
c
n
t
1
cnt_0,cnt_1
cnt0,cnt1分别表示
0
0
0和
1
1
1的数量。假设序列是如下形式:
111...10111..10111..1.....111..10111...1
111...10111..10111..1.....111..10111...1
111...10111..10111..1.....111..10111...1,即假设第
i
−
1
i-1
i−1个
0
0
0与第
i
i
i个
0
0
0之间的
1
1
1的数量是
c
t
i
ct_i
cti,我们只要求出
c
t
1
∼
c
n
t
0
ct_{1\sim cnt_0}
ct1∼cnt0就能求出整个序列了。
首先考虑 c t 1 ct_1 ct1怎么求,显然第1段连续的 1 1 1的右边一定有 c n t 0 cnt_0 cnt0个 0 0 0,不妨先询问 c n t 0 个 0 1 0000..0 ⏞ \begin{matrix} cnt_0个0\\1 \overbrace{ 0000..0 } \end{matrix} cnt0个010000..0 ,如果回答是 y e s yes yes,那么说明 c t 1 ≥ 1 ct_1\ge 1 ct1≥1,然后继续询问 c n t 0 个 0 11 0000..0 ⏞ \begin{matrix} cnt_0个0\\11 \overbrace{ 0000..0 } \end{matrix} cnt0个0110000..0 ,直到询问到回答 n o no no的时候,就能知道 c t 1 ct_1 ct1是多少了。
不过上面的想法其实太理想了,要注意到题目给出了个一个限制条件就是询问长度不能大于 ⌊ n 2 ⌋ + 1 \lfloor \frac n2\rfloor +1 ⌊2n⌋+1,假设这个长度是 k k k,那么当询问长度超过 k k k的时候就不合法。如果没有这个限制条件,其实本题就已经做出来了,到这里题解也就结束了,因为求 c t i ct_i cti的时候,我们可以考虑询问如下形式: ∑ j = 1 i c t j 个 1 , c n t 0 − i + 1 个 0 1111...1 ⏞ 000...0 ⏞ \begin{matrix}\small{\sum_{j=1}^{i}ct_j个1,\;cnt_0-i+1个0}\\ \overbrace{1111...1}\qquad\qquad\overbrace{000...0}\end{matrix} ∑j=1ictj个1,cnt0−i+1个01111...1 000...0 ,左边的1的数量达到 ∑ j = 1 i c t j + 1 \sum_{j=1}^ict_j+1 ∑j=1ictj+1的时候询问会得到 n o no no,这时候就能得到 c t i ct_i cti了。
那么加上限制后,我们可以改变询问的方式,可以考虑求解反面,即 1 − ∑ j = 1 i c t j 1-\sum_{j=1}^{i}ct_j 1−∑j=1ictj,可以用 i 个 1 000..0 ⏞ 11..1 \begin{matrix}i个1\\\overbrace{000..0}11..1\end{matrix} i个1000..0 11..1来求解这个数量,那么是否会超过 k k k的限制呢,可以算一下,方便起见设当前确定的 1 1 1和确定的 0 0 0的数量是 c u r 1 , c u r 0 cur_1,cur_0 cur1,cur0,那么第一种询问长度就是 l e n 1 = c u r 1 + c t i + c n t 0 − i + 1 len_1=cur_1+ct_i+cnt_0-i+1 len1=cur1+cti+cnt0−i+1,第二种询问长度就是 l e n 2 = i + c n t 1 − c u r 1 − c t i len_2=i+cnt_1-cur_1-ct_i len2=i+cnt1−cur1−cti,两者相加满足 l e n 1 + l e n 2 = c n t 0 + c n t 1 = n len_1+len_2=cnt_0+cnt_1=n len1+len2=cnt0+cnt1=n,也就是总有一个长度一定是满足小于等于 k k k的,也就是说总有一个询问方式是可行的,那么可以先试第一种询问方式,然后再第一种询问方式的基础上继续第二种询问方式即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2000+5;
const int inf = 2147483647;
typedef long long ll;
int n,k,f,cnt0,cnt1;
int qry(string s){
for(int i=0;i<s.length();++i){
s[i]=((s[i]-'0')^f)+'0';
}
cout<<"? "<<s<<'\n';
fflush(stdout);
int u;
cin>>u;
return u;
}
int answer(string s){
cout<<"! "<<s<<'\n';
fflush(stdout);
}
int getnum(){
int l=1,r=k-1,ans=0;
while(l<=r){
int mid=l+r>>1;
if(mid==ans || qry(string(mid,'0'))){
l=mid+1;
ans=mid;
}else r=mid-1;
}
return ans;
}
int cal(int cur0,int cur1){
int i=1,res0=cnt0-cur0,res1=cnt1-cur1;
while(i+cur1+res0<=k && i<=res1){
if(!qry(string(i+cur1,'1')+string(res0,'0')))return i-1;
i++;
}
i--;
if(i==res1)return i;
i=cnt1-cur1-i;
while(i>=0){
if(qry(string(cur0+1,'0')+string(i,'1')))return cnt1-cur1-i;
i--;
}
return cnt1-cur1-i;
}
int main(){
cin>>n;
k=n/2+1;
f=qry(string(k,'0'));
cnt0=getnum(),cnt1=n-cnt0;
int cur0=0,cur1=0;
string ans;
while(cur0<cnt0){
int now=cal(cur0,cur1);
ans+=string(now,'1');
ans+='0';
cur1+=now;
cur0++;
}
ans+=string(cnt1-cur1,'1');
for(int i=0;i<ans.length();++i){
ans[i]=((ans[i]-'0')^f)+'0';
}
answer(ans);
}