关于此题[ABC298E] Unfair Sugoroku 概率DP的一些总结

传送门
题目大意:两个人小T和小A分别从A点和B点开始,分别等概率地每次可以往后走\(1,2...P\)\(1,2...Q\)步,小T先走,问小T先走到N位置及以后的概率是多少。

难以想到如何设计状态,由于我们同时要对小T和小A的位置进行维护,所以应该考虑将其分别设为同一个数组的两维来进行维护。我们设\(f[i][j]\)表示当前小T在i位置,小A在j位置的概率。
我们将两人分别往后走看作一个一个回合,在每个回合中小T先走,不管小T是否走到N后小A都也往后走。于是想到可以用当前状态来更新后面的状态。

\(f[min({i + k1,n})][min({j + k2,n})] += f[i][j]\)

而对于当前\(f[i][j]\),我们可以从\(f[i-1][j] + ... + f[i - p][j]\)

\(f[i][j] = \frac{1}{p}(f[i-1][j] + f[i-2][j] + ... +f[i-p][j])\)
\(f[i][j] = \frac{1}{q}(f[i][j-1] + f[i][j-1] + ... +f[i][j-q])\)
合并起来看
又由于要求逆元,分母应该为\(p * q\)
所以我们在DP的时候就对于每一个\(i\),\(j\),用它来往后转移更新时都应该注意求逆元而不能到最后求总和再除
还要注意的是,最后累计求概率的时候,我们应该求\(\sum_{i=b}^{n}f[n][i]\),要加上\(f[n][n]\)是因为前面说过,在每个回合当中我们看作小T先动,不管此时小T是否到了N后面小A也往后走,而这样的情况下也是符合条件的。

#include<bits/stdc++.h>

using namespace std;

const long long mod = 998244353;
long long t,ans;
const long long N = 2e5 + 10;
long long n,a,b,p,q;
long long f[110][110];

long long quickMul(long long x,long long k) {
    if(k == 1) return x;
    if(k == 0) return 1;
    long long tmp = quickMul(x,k/2);
    tmp = (tmp * tmp) % mod;
    if(k % 2) tmp = (tmp * x) % mod;
    return tmp;
}

void solve() {
    cin >> n >> a >> b >> p >> q;
    f[a][b] = 1;
    for(long long i = a;i <= n - 1;i++) {
        for(long long j = b;j <= n - 1;j++) {
            for(long long k1 = 1;k1 <= p;k1++) {
                for(long long k2 = 1;k2 <= q;k2++) {
                    f[min(n,i + k1)][min(n,j + k2)] = (f[min(n,i + k1)][min(n,j + k2)] + f[i][j] * quickMul(p * q,mod - 2) % mod) % mod;
                }
            }
        }
    }
    for(long long i = b;i <= n;i++)
        ans += f[n][i],ans %= mod;
    cout << ans;
}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    t = 1;
    while(t--) solve();

    return 0;
}
posted @ 2025-01-16 21:52  孤枕  阅读(10)  评论(0)    收藏  举报