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));
}
posted @ 2024-11-30 23:57  丝羽绫华  阅读(41)  评论(0)    收藏  举报