Luogu P10384 「HOI R1」杂交选种 题解 [ 紫 ] [ 概率论 ] [ 交互 ]
杂交选种:我是赛博孟德尔.jpg

很有趣的题。
首先注意到 \(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;
}

浙公网安备 33010602011771号