Luogu P10384 「HOI R1」杂交选种 题解 [ 紫 ] [ 概率论 ] [ 交互 ]

杂交选种:我是赛博孟德尔.jpg

image

很有趣的题。

首先注意到 \(4.5\times 10^5 \approx 2\times 10^4 \times 20\),相当于均摊下来每个基因被杂交二十次。

然后弱化条件,考虑部分分,即当至少有一个 \(\texttt{aa}\) 的时候的策略是什么,显然这时候直接拿 \(\texttt{aa}\) 和其他基因测交 \(20\) 次即可,如果是 \(\texttt{AA}\)永远无法生出 \(\texttt{aa}\),如果是 \(\texttt{Aa}\) 则有 \(\dfrac{1}{2}\) 的概率生出 \(\texttt{aa}\)。因此测交 \(20\) 次的失败率为 \(\dfrac{1}{2^{20}}\),对于 \(2\times 10^4\) 的数据,成功率约为 \(98.1\%\),足以通过。

进而考虑如何做全是 $\texttt{AA}, \texttt{Aa} $ 的情况。显然判断这两种必须合成出一个 \(\texttt{aa}\),而合成 \(\texttt{aa}\) 之后就可以按上面的做法直接测交了,于是我们只需合成出一个 \(\texttt{aa}\) 即可。

注意到将一个非 \(\texttt{aa}\) 的基因 \(X\) 与某个非 \(\texttt{aa}\) 的基因 \(Y\) 杂交的后代 \(Z\) 与该基因杂交,假设该后代为 \(Z'\),然后再拿 \(Z'\)\(X\) 杂交得到 \(Z''\),如此循环往复下去,如果试验 \(20\) 次,那么 \(X=\texttt{Aa},Y=\texttt{AA}\) 时生出 \(\texttt{aa}\) 的概率是很大的,写了个 DP 后发现这个概率是 \(94.9\%\),DP 代码如下:

/*
dp_0 表示 AA
dp_1 表示 Aa
dp_2 表示 aa
*/
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
ldb dp[200][3];
int main()
{
    dp[0][0]=1;
    for(int i=0;i<100;i++)
    {
        dp[i+1][2]+=dp[i][2];
        dp[i+1][0]+=dp[i][0]*0.5;
        dp[i+1][1]+=dp[i][0]*0.5;
        dp[i+1][0]+=dp[i][1]*0.25;
        dp[i+1][2]+=dp[i][1]*0.25;
        dp[i+1][1]+=dp[i][1]*0.5;
    }
    for(int i=1;i<=100;i++)
        printf("%d : %.10Lf%%\n",i,dp[i][2]*100);
    return 0;
}

剩下有一个细节是如果 \(Z\) 直接就是 \(\texttt{aa}\) 了,那么也是可以证明 \(X\)\(\texttt{Aa}\),因为只有两个都是 \(\texttt{Aa}\) 的情况才能生出 \(\texttt{aa}\)

因此对每个基因进行验证,只要合成出了一个 \(\texttt{aa}\) 就直接测交,那么成功率就是第一个 \(\texttt{Aa}\) 生出 \(\texttt{aa}\) 的成功率,即 \(94.9\%\),那么整体的成功率就是 \(94.9\%\times 98.1\%=93.1\%\),可能有点偏差,因为实际测试 \(10\) 次内有 \(7\) 次 AC。

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
vector<string>ans;
char query(int k);
void cross(int i,int j);
vector<string> guess(int n)
{
    int cnt=n,p=0;
    for(int i=1;i<=n;i++)
    {
        if(query(i)=='a')
        {
            p=i;
            break;
        }
    }
    for(int i=1;i<=n;i++)
        ans.push_back("");
    if(p==0)
    {
        int npos=n+1;
        for(int i=1;i<=n;i++)
        {
            int x=++cnt;
            cross(i,i%n+1);
            bool lg=0;
            for(int j=1;j<=20;j++)
            {
                if(query(cnt)=='a')
                {
                    lg=1;
                    ans[i-1]="Aa";
                    p=cnt;
                    npos=i+1;
                    break;
                }
                cross(x,i);
                x=++cnt;
            }
            if(lg==0)ans[i-1]="AA";
            if(p)break;
        }
        for(int i=npos;i<=n;i++)
        {
            bool lg=0;
            for(int j=1;j<=20;j++)
            {
                cross(i,p);
                if(query(++cnt)=='a')
                {
                    ans[i-1]="Aa";
                    lg=1;
                    break;
                }
            }
            if(lg==0)ans[i-1]="AA";
        }
        return ans;
    }
    for(int i=1;i<=n;i++)
    {
        if(query(i)=='a')
        {
            ans[i-1]="aa";
            continue;
        }
        bool lg=0;
        for(int j=1;j<=20;j++)
        {
            cross(i,p);
            if(query(++cnt)=='a')
            {
                ans[i-1]="Aa";
                lg=1;
                break;
            }
        }
        if(lg==0)ans[i-1]="AA";
    }
    return ans;
}
posted @ 2025-07-14 23:59  KS_Fszha  阅读(15)  评论(0)    收藏  举报