ZS Shuffles Cards 题解

ZS Shuffles Cards 题解

我们把每一次抽一些数字牌再抽到 joker 视作一局游戏。

每局期望轮数

首先考虑 fi 表示每一局游戏抽出 i 张牌的概率。

那么就是先抽出 i1 张数字牌,再抽出一张 joker 。

概率就是 :

fi=mn+mi+1k=0i2nkm+nk

一局游戏期望抽牌(轮)数量也就是:

E=i=1n+1i×fi=i=1n+1i×mn+mi+1k=0i2nkm+nk

直接算这个式子会超时,我们可以先预处理后面的部分:

gi=k=0i2nkm+nk

可得

gi+1=gini+1m+ni+1

就转化成

E=i=1n+1i×mn+mi+1×gi+1

期望局数

hi 表示集合 S 内还缺 i 个数才凑够 n 时,期望再玩多少局。

显然答案就是 hn

若抽到了缺少的牌,那么就是:

km+kfk1

若抽到鬼牌,那么就是(加 1 是因为新的一局):

mm+kfk+1

所以就是:

fk=mm+k(fk+1)+km+kfk1

化简得:

fk=fk1+mk

初始 f0=1

因为即使你的集合内的数已经凑够了,你也要抽到 joker 才能停止。

最后答案就是

ans=E×fn

温馨提示

建议预处理逆元(我最开始没有预处理虽然本地没超时但测评会T)

code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 998244353;
const int N = 4e6 + 10;
ll n, m, w[N], ans1 = 0, ans2 = 0;
ll pr[N], ip[N], rv[N];
inline ll mpow(ll x, ll k){
    ll ans = 1;
    while(k){
        if(k & 1) ans = ans * x % mod;
        k >>= 1;
        x = x * x % mod;
    }
    return ans;
}
void pre(int n){
    pr[0] = 1;
    for(int i = 1; i <= n; ++i) pr[i] = pr[i - 1] * i % mod;
    ip[n] = mpow(pr[n], mod - 2);
    for(int i = n - 1; i >= 1; --i) ip[i] = ip[i + 1] * (i + 1) % mod;
    for(int i = 1; i <= n; ++i) rv[i] = ip[i] * pr[i - 1] % mod;
}
void op(){
    w[1] = 1;
    for(int i = 1; i <= n + 1; ++i){
        w[i + 1] = w[i] * (n - i + 1) % mod * rv[n + m - i + 1] % mod;
        ans1 = (ans1 + i * m % mod * rv[n + m - i + 1] % mod * w[i] % mod) % mod;
    }
    ans2 = m + 1;
    for(int i = 2; i <= n; ++i){
        ans2 = (ans2 + m * rv[i] % mod) % mod;
    }
}
int main(){
    cin>> n >> m;
    pre(n + m + 1);
    op();
    cout<<ans1 * ans2 % mod;
    return 0;
}

posted @   He_Zi  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示