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

传送门
题目大意:两个人小T和小A分别从A点和B点开始,分别等概率地每次可以往后走12...P12...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[i1][j]+...+f[ip][j]

f[i][j]=1p(f[i1][j]+f[i2][j]+...+f[ip][j])
f[i][j]=1q(f[i][j1]+f[i][j1]+...+f[i][jq])
合并起来看
又由于要求逆元,分母应该为pq
所以我们在DP的时候就对于每一个i,j,用它来往后转移更新时都应该注意求逆元而不能到最后求总和再除
还要注意的是,最后累计求概率的时候,我们应该求i=bnf[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 @   孤枕  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
我命令你,喜欢我!