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

目录
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 赢的概率。
求到某个点的概率题,通常都会需要用到类似:
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]](https://1000bd.com/contentImg/2024/03/15/113121564.png)
同理:
![dp[i][j][1] = \frac{1}{q}\sum_{k=1}^{q}dp[i][min(k + k, N)][0]](https://1000bd.com/contentImg/2024/03/15/113121568.png)
最后输出 打派【a】【b】【0】即可。
- #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];
- }