题目

传送门

解法

\(dp_{i,j}\) 为识别前 \(i\) 首歌,需要 \(j\) 秒的概率(为什么会在后面提及)。容易想到枚举第 \(i\) 首歌已经听了 \(k\) 秒来转移:

\[dp_{i,j}=\sum_{k=1}^{t_i-1}dp_{i-1,j-k}\times (1-p_i)^{k-1}\times p_i \]

这是 \(\mathcal O(n^3)\) 的。但是这个方程的 \(k\) 在指数上,也就是说,不同的 \(j\) 会引起系数的 整体 变化!具体地说,考虑 \(dp_{i,j-1}\)\(dp_{i,j}\) 的变化:在 \(j-1\)\(k\in[1,t_i-1]\),转移到 \(j\) 相当于 \(k\in[2,t_i]\)。于是我们需要补 \(k=1\) 那一项,还要去除 \(t_i\) 那一项。这样复杂度降到了 \(\mathcal O(n^2)\)

每首歌的贡献为 \(1\),所以直接将概率相加即可。

代码

#include <cstdio>

const int N = 5005;

int n, T, t[N];
double p[N], f[N][N], ans;

int read() {
    int x = 0, f = 1; char s;
    while((s = getchar()) < '0' || s > '9') if(s == '-') f = -1;
    while(s >= '0' && s <= '9') {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
    return x * f;
}

double qkpow(double x, int y) {
    double r = 1;
    while(y) {
        if(y & 1) r *= x;
        x *= x; y >>= 1;
    }
    return r;
}

int main() {
    n = read(), T = read();
    for(int i = 1; i <= n; ++ i) p[i] = 1.0 * read() / 100, t[i] = read();
    f[0][0] = 1;
    for(int i = 1; i <= n; ++ i) {
        double sum = 0, w = qkpow(1 - p[i], t[i] - 1);
        for(int j = 1; j <= T; ++ j) {
            sum *= 1 - p[i];
            sum += f[i - 1][j - 1] * p[i];
            if(j >= t[i]) {
                sum -= f[i - 1][j - t[i]] * w * p[i];
                f[i][j] += f[i - 1][j - t[i]] * w;
            }
            f[i][j] += sum; ans += f[i][j];
        }
    }
    printf("%.9f\n", ans);
    return 0;
}
posted on 2020-04-11 15:24  Oxide  阅读(34)  评论(0编辑  收藏  举报