acm-(交互题、思维、二分、子序列、好题)2020 年 “游族杯” 全国高校程序设计网络挑战赛 B. Binary String

题面
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 i1 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} ct1cnt0就能求出整个序列了。

首先考虑 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} cnt0010000..0 ,如果回答是 y e s yes yes,那么说明 c t 1 ≥ 1 ct_1\ge 1 ct11,然后继续询问 c n t 0 个 0 11 0000..0 ⏞ \begin{matrix} cnt_0个0\\11 \overbrace{ 0000..0 } \end{matrix} cnt00110000..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=1ictj1,cnt0i+101111...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 1j=1ictj,可以用 i 个 1 000..0 ⏞ 11..1 \begin{matrix}i个1\\\overbrace{000..0}11..1\end{matrix} i1000..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+cnt0i+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+cnt1cur1cti,两者相加满足 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);
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值