题解:CF1765G Guess the String

· · 题解

注意到 789>\frac 3 4 n,这启示我们可能要找一个期望询问次数 \frac 3 4 n 的随机做法。

先询问 2 2 直接获得 s_2。(这一步用 1 询问也行,不过由于 q 数组的性质以及 s_1=0,询问 2 2 的结果直接就是 s_2)。

然而,随机化做法不能只看期望,否则你会像我一样靠期望得到了一个似乎正确的做法却 WA(不是这个题)。我们还要算一下错误率。上面的做法一定能得到最后的答案,所以错误只会出现在询问次数 >789 的时候。假定 n 是偶数。n 是奇数的时候显然错误率更低。我们设询问了 x 次,那么显然有 (x-n) 组用了 2 次、(2n-x) 组用了 1 次,概率 \mathrm{C}^{x-n}_{\frac n 2} \times (\frac 1 2)^{x-n}\times (\frac 1 2)^{\frac n 2 - (x-n)}

x > 789,总的错误率为

\sum_{x=790-n}^{\frac n 2} \mathrm{C}^{x}_{\frac n 2} \times (\frac 1 2)^{\frac n 2}

,当 n=1000 时约为 p=2 \times 10^{-4}n 更小的点错误率只会更低。本题共 78 个测试点,则总的正确率为 (1-p)^{78} \approx 98.5 \%。如果这都不过,那你会为省选攒下大量 rp。

祝大家 rp++,正赛随机化都能过。

:::info[Code Time]

#include <bits/stdc++.h>
using namespace std;
const int N=1007;
mt19937 rnd((size_t)(random_device()()+time(0)));
int s[N],res;
int query(int tp,int x){
    cout<<(tp+1)<<' '<<x<<endl; // tp=0 的询问应该以 1 开头,tp=1 的询问应该以 2 开头。
    cin>>res;
    return res;
}
void answer(int n){
    cout<<"0 ";
    for(int i=1;i<=n;i++) cout<<s[i];
    cout<<endl;
    cin>>res; // 回答完之后需要读入一个表示该组数据正确与否的 1。
}
int main(){
    int t;
    cin>>t;
    while(t-->0){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++) s[i]=-1; // 多测清空,初始化为 -1。
        s[1]=0; // 题目要求 s[1]=0。
        s[2]=query(1,2);
        for(int i=4;i<=n;i+=2){
            int tp=rnd()&1;
            int u=query(tp,i);
            if(u>=2) s[i-1]=s[u-1]^tp,s[i]=s[u]^tp; // 一定要先赋值 i-1。
            else if(u==1) s[i]=tp;
            else s[i]=tp^1;
            if((s[i]==(s[2]^tp))&&u<2) s[i-1]=tp^1;
            if(!~s[i-1]){
                u=query(0,i-1);
                if(u) s[i-1]=s[u];
                else s[i-1]=1;
            }
        }
        if(n&1){
            int u=query(0,n);
            if(u) s[n]=s[u];
            else s[n]=1;
        }
        answer(n);
    }
}

// rp++

:::