CodeForces

题目

传送门

解法

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

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

这是 (mathcal O(n^3)) 的。但是这个方程的 (k) 在指数上,也就是说,不同的 (j) 会引起系数的 整体 变化!具体地说,考虑 (dp_{i,j-1})(dp_{i,j}) 的变化:在 (j-1)(kin[1,t_i-1]),转移到 (j) 相当于 (kin[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
", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/12679930.html