题目
解法
设 \(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;
}