CF2037E 题解

· · 题解

不错的题。

首先,不能这么做:

这样做的不足之处:

我们发现最多询问 n 次,那么整体就是线性的,我们不妨考虑如何 \Theta(n) 地进行推导。接下来设 f(l,r) 表示对 [l,r] 的询问。

可以发现如果 f(1,i)=0 但是 f(1,i + 1)=k,k>0 的话,我们可以得到如下结论:

综上,我们可以得出:此时 [1,i] 一定是由一段前缀的 1 和一段后缀的 0 组成,可以没有 1

那么后缀的 0 的长度是多少呢?可以发现,如果 i+1 仍然是 0,那么 f(1,i+1)=0,所以位置 i+1 一定是 1;由于 f(1,i+1)=k,那么意味着(注意到只有这个 1 才有机会和前面的 0 匹配)这个 1k0 匹配了,所以后缀的 0 的长度是 k,然后顺便直接把前面的 1 填进去就好了。

之后该怎么办呢?注意到另一件事,就是左端点不动,右端点单增时,答案不可能变小,那么我们找到第一个满足上述条件的位置 i,之后对于 i 后面的位置 j

那么我们直接再扫一遍就可以了。

什么时候没有唯一解呢?就是没有找到 i 的时候,很好理解。

代码:

#include <bits/stdc++.h>
#define f(i ,m ,n ,x) for (int i = (m) ; i <= (n) ; i += (x))
using std :: cin ; using std :: cout ; using std :: endl ;
int t ,n ,ch[10007] ; 
int main () {
    cin >> t ;
    while (t --) {
        cin >> n ;
        int ans = 0 ; bool flag (0) ;
        cout << "? " << 1 << ' ' << 2 << endl ;
        cin >> ans ;
        if (ans == 1) { ch[1] = 0 , ch[2] = 1 ,flag = true ;}
        f (i ,3 ,n ,1) {
            cout << "? " << 1 << ' ' << i << endl ;
            int res ; cin >> res ;
            if (flag == false) {
                if (res ^ 0) {
                    f (j ,1 ,i - 1 - res ,1) ch[j] = 1 ;
                    f (j ,i - res ,i - 1 ,1) ch[j] = 0 ;
                    ch[i] = 1 , flag = true ;
                } 
                ans = res ;
            } else {
                if (res == ans) ch[i] = 0 ;
                else ch[i] = 1 ;
                ans = res ;
            }
        }
        if (ans == 0) cout << "! IMPOSSIBLE" << endl ;
        else {
            cout << "! " ;
            f (i ,1 ,n ,1) cout << ch[i] ; cout << endl ;
        }
    }
    return 0 ;
}