P5249 [LnOI2019] 加特林轮盘赌

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;
}
posted @ 2023-12-16 11:27  123wwm  阅读(28)  评论(0)    收藏  举报