CF2037E 题解
CF2037E 题解
题意
给定一个长度为 \(n\) 的 \(01\) 串,定义 \(f(l,r)\) 为 \(l\) 到 \(r\) 区间内 \(01\) 子序列的数量,通过最多 \(n\) 次交互,确定这个 \(01\) 串的构成。
分析
可以从莫队的思想,也就是增量,来思考如何解决。
如果说我们已经知道了 \(f(l,r)=ans\) ,接下来我们询问 \(f(l,r+1)\) 的值,相当于往 \([l,r]\) 的串的后面接了一个字符上去。
- 如果这一次询问的答案不同于 \(ans\) ,说明有新的 \(01\) 子序列产生,那么一定是引入了一个 \(1\) 导致的。
- 如果这一次询问的答案和 \(ans\) 相同,那么有两种情况。
- \(r+1\) 位引入的是一个 \(0\)
- \(r+1\) 位引入的是一个 \(1\) ,但是 \([l,r]\) 内一个 \(0\) 也没有。
为了方便,我们直接利用上述原理从左到右构建这个串,也就是从小到大地询问 \(f(1,i) , 2\le i\le n\) 的答案。
那么首先需处理第一个答案非零的位置,这说明当前这一位一定是 \(1\) ,并且前面是 \(11...100..0\) 的结构(因为前面的询问得到的答案都是 \(0\) ),具体来说,前面 \(0\) 的个数就是当前这一次询问的答案(注意前面不一定有 \(1\) ,但一定有 \(0\) )。
接着我们就可以继续安心地询问了,由于我们确保了已经有若干个 \(0\) 出现在串中,那么如果当前得到的答案和上一次相同,当前位就一定是 \(0\) ;否则就是 \(1\) 。
唯一一种无解的情况,就是我们始终没有询问到一个非零的答案,这样的话这个串可能是 \(1111..1111\) ,或者 \(000..000\) ,亦或是 \(11..11000...000\) ,所以是无法确定的。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
inline int q(int l,int r){cout<<"? "<<l<<' '<<r<<endl;int ans;cin>>ans;return ans;}
char s[100010];
inline void solve()
{
cin>>n;
int p=2,las_ans;
while(p<=n)
{
int ans=q(1,p);
if(ans!=0)
{
s[p]='1';
for(int i=1;i<=p-1-ans;++i)s[i]='1';
for(int i=p-ans;i<=p-1;++i)s[i]='0';
las_ans=ans;
p++;
goto NEXTSTEP;
}
p++;
}
cout<<"! IMPOSSIBLE"<<endl;return ;
NEXTSTEP:;
while(p<=n)
{
int ans=q(1,p);
if(ans==las_ans)
{
s[p]='0';
}
else
{
s[p]='1';
las_ans=ans;
}
p++;
}
cout<<"! ";
for(int i=1;i<=n;++i)cout<<s[i];
cout<<endl;
}
signed main()
{
int T;
cin>>T;
while(T--)
solve();
return 0;
}
为什么要练,为什么要写?
引用一句让我幡然悔悟的话:
“练了不一定写的出来正解,不练一定写不出来正解”
本文来自博客园,作者:Hanggoash,转载请注明原文链接:https://www.cnblogs.com/Hanggoash/p/18555694

浙公网安备 33010602011771号