[补题记录] Atcoder Beginner Contest 298(E)

URL:https://atcoder.jp/contests/abc298

[补题记录] Atcoder Beginner Contest 298(E)_第1张图片

目录

E

Problem/题意

Thought/思路

Code/代码


E

Problem/题意

A、B 轮流投色子,A 投出 [1, P] 点数的概率相等,B投出 [1, Q] 点数的概率相等。

现有 N 个点,初始时,A 位于点 a,B 位于点 b。假设 A 或 B 现在在点 X,每投出一个点数 i,则 A 或 B 会走到 min(X + i, N),先走到 N 的获胜。

问 A 先投色子,A 赢的概率。

Thought/思路

求到某个点的概率题,通常都会需要用到类似:

dp[i] = 1 / P * (dp[i + A1] + dp[i + A2] + ... + dp[i + Ap])

这种形式的状态转移方程,意为:我知道了后几个点的期望,可以反推从前面的点出发,然后获胜的期望。

这道题就是多了一个人,使得状态转移不仅是简单的从后面的转移到前面的,而是需要考虑前一步是由谁来做的,那么就应该从谁哪里转移过来


dp[i][j][0]:A 在 i,B 在 j,由 A 投色子A 获胜的概率

dp[i][j][1]:A 在 i,B 在 j,由 B 投色子A 获胜的概率

dp[i][j][0] 的前一步是由 B 做出的,并且 A 在点 i 的状态,是由前一步的所有 A 可能到的点的状态转移过来的。因此:

dp[i][j][0] = \frac{1}{p}\sum_{k=1}^{p}dp[min(i + k, N)][j][1]

同理:

dp[i][j][1] = \frac{1}{q}\sum_{k=1}^{q}dp[i][min(k + k, N)][0]

最后输出 打派【a】【b】【0】即可。

Code/代码

#include "bits/stdc++.h"

#define int long long

const int mod = 998244353;

int n, a, b, p, q, dp[107][107][2];

int ksm(int a, int b) {
	int res = 1;
	while (b > 0) {
		if (b & 1) res = res * a % mod;
		a = a * a % mod;
		b /= 2;
	}
	return res % mod;
}

int inv(int a, int p) {
	return ksm(a, p - 2);
}

signed main() {
	std::cin >> n >> a >> b >> p >> q;
	for (int i = 1; i <= n; ++ i) {
		dp[n][i][0] = 1; // A 在 N,A 赢的概率肯定是 100%
		dp[n][i][1] = 1;
		dp[i][n][0] = 0; // B 在 N,A 赢的概率肯定是 0%
		dp[i][n][1] = 0; 
	}

	for (int i = n - 1; i >= 1; -- i) {
		for (int j = n - 1; j >= 1; -- j) {
			for (int k = 1; k <= p; ++ k) { // 这一轮由 A 走,那么 A 赢的概率,需要从上一轮 B 走且 A 走到 min(i + k, n) 且 A 赢的概率转移过来
				dp[i][j][0] = (dp[i][j][0] + dp[std::min(i + k, n)][j][1]) % mod;
			}
			dp[i][j][0] = dp[i][j][0] * inv(p, mod) % mod;

			for (int k = 1; k <= q; ++ k) { 
				dp[i][j][1] = (dp[i][j][1] + dp[i][std::min(j + k, n)][0]) % mod;
			}
			dp[i][j][1] = dp[i][j][1] * inv(q, mod) % mod;
		}
	}

	std::cout << dp[a][b][0];
}

你可能感兴趣的:(补题记录,动态规划,算法)