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\) 相同,那么有两种情况。
    1. \(r+1\) 位引入的是一个 \(0\)
    2. \(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;
}
posted @ 2024-11-19 22:05  Hanggoash  阅读(47)  评论(0)    收藏  举报
动态线条
动态线条end