AtCoder Beginner Contest 382 E Solution
闲话
比 F 难,但是有意义。
题解
为了求得期望,我们显然需要先知道拆开一个包装得到各种数量的稀有卡牌的概率。
用 DP 求出这些概率是简单的。具体的,设 \(r_i\) 为拆开一个包装得到 \(i\) 个稀有卡牌的概率。为方便,以下将 \(p_j\) 转换为实际概率而非百分数。初始时,\(r_0=1\)。
对于第 \(i\) 张卡牌,对于 \(1\le j\le i\),同时执行如下操作:
\[r_j\leftarrow p_ir_{j-1}+(1-p_i)r_j
\]
特别的,\(r_0\leftarrow (1-p_i)r_0\)。
完成上述计算后,考虑如何计算最终期望。
令 \(f(x)\) 为获得 \(x\) 个特殊卡牌的拆包数量期望。显然:
\[f(x)=\begin{cases}
0 & x\le 0\\
\sum_{i=0}^n r_i(f(x-i)+1) & \text{otherwise}\\
\end{cases}
\]
上述式子在 \(r_0=0\) 的情况下是非常好求的。问题就在于,有时候 \(r_0\neq 0\)。
这种情况下,为了避免无限递归计算,我们需要拆一下式子。
\[\begin{aligned}
f(x)= & \sum_{i=0}^n r_i(f(x-i)+1)\\
f(x)= & \space r_0(f(x)+1)+\sum_{i=1}^n r_i(f(x-i)+1)\\
(1-r_0)f(x)= & \space r_0+\sum_{i=1}^n r_i(f(x-i)+1)\\
f(x)= & \frac{r_0+\sum_{i=1}^n r_i(f(x-i)+1)}{1-r_0}
\end{aligned}
\]
由于题目保证 \(r_0\neq 1\),上述转移式可行。
使用记忆化 DFS 可以简单地算出上述表达式的值。
最终答案即为 \(f(x)\)。
时间复杂度 \(O(n^2+nx)\)。
代码
#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 5e3 + 10;
int n, m;
using ld = long double;
ld psb[N], a[N], rel[N];
bool vis[N];
ld trc(int x)
{
if (x <= 0)
return 0;
if (vis[x])
return rel[x];
ld expc = 0;
for (int i = n; i; i--)
{
expc += psb[i] * (trc(x - i) + 1);
}
expc = (expc + psb[0]) / (1 - psb[0]);
vis[x] = true;
// trc(x)=psb[0]*(trc(x)+1)+rel
// (1-psb[0])*trc(x)=psb[0]+rel
//
return rel[x] = expc;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%Lf", a + i);
a[i] *= 0.01;
}
psb[0] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = i; j; j--)
{
psb[j] = psb[j] * (1 - a[i]) + psb[j - 1] * a[i];
}
psb[0] *= (1 - a[i]);
}
psb[0] = max(psb[0], a[0]);
// for (int i = 0; i <= n; i++)
// {
// printf("%.16Lf\n", psb[i]);
// }
printf("%.16Lf\n", trc(m));
}

浙公网安备 33010602011771号