P5249 [LnOI2019] 加特林轮盘赌
考虑 dp,设 \(f_{i,j}\) 为在 \(i\) 只长脖子鹿中第 \(j\) 只获胜的概率。
若 \(j\) 不为第 \(1\) 个时,由第 \(1\) 个死或不死转移而来;若 \(j\) 为第 \(1\) 个时,自己不能死,所以只能从 \(f_{i,i}\) 转移而来,注意下一轮 \(j\) 变为了第 \(i\) 个。有
\[f_{i,j}=\begin{cases}
(1-p)⋅f_{i,j-1}+p⋅f_{i-1,j-1} & j>1 \\
(1-p)⋅f_{i,i} & j=1
\end{cases}\]
观察方程式,假设前 \(i-1\) 已求完,我们会发现这 \(i\) 个状态相互之间有依赖关系,而且这个关系刚好为一个环。
那么我们可以假设 \(f_{i,1}\) 为 \(x\)。则 \(f_{i,2}\) 为 \((1-p)⋅x+p⋅f_{i-1,1}\),以此类推可以得出 \(f_{i,i}\) 一定能写成 \(ax+b\) 的形式,又有 \(x=(1-p)⋅f_{i,i}\),所以能解出 \(f_{i,1}\),再递推出其他状态即可。
这题空间有点紧,所以我们可以用滚动数组把 \(i\) 这一维滚掉。
代码:
#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mk make_pair
#define ll long long
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
inline int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') f = c == '-' ? -1 : f, c = getchar();
while (c >= '0' && c <= '9') x = (x<<3)+(x<<1)+(c^48), c = getchar();
return x*f;
}
inline void write(int x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x/10);
putchar('0'+x%10);
}
const int N = 1e4+5;
const double eps = 1e-12;
int n, k, id = 1;
double p, a[N], b[N], f[2][N];
int main() {
scanf("%lf%d%d", &p, &n, &k);
if (fabs(p) < eps) puts(k == 1 ? "1" : "0"), exit(0);
f[0][1] = 1; a[1] = 1;
for (int i = 2; i <= n; ++i) {
for (int j = 2; j <= i; ++j) {
a[j] = a[j-1]*(1-p);
b[j] = b[j-1]*(1-p)+f[id^1][j-1]*p;
}
f[id][1] = (1-p)*b[i]/(1-(1-p)*a[i]);
for (int j = 2; j <= i; ++j) f[id][j] = f[id][j-1]*(1-p)+f[id^1][j-1]*p;
id ^= 1;
}
printf("%.8lf", f[id^1][k]);
return 0;
}