URL:https://atcoder.jp/contests/abc298
目录
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 可能到的点的状态转移过来的。因此:
同理:
最后输出 打派【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];
}