ZJOI模拟 绝对伏特加【数学期望+组合数学+生成函数】

本文介绍了一种计算骰子游戏中特定概率的方法。通过数学建模和算法设计,解决了多轮骰子游戏中数值出现次数的期望值计算问题。利用概率论和组合数学的知识,实现了高效的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

AlanAlan在玩骰子游戏,AlanAlan会玩nn轮骰子,每轮的数值在[1,K][1,K]中随机出现。记aiai表示nn轮投掷中,数值ii出现的次数,求aF1aF2aFLa1F∗a2F∗……aLF的期望。答案对2003取模。
1n,k109,LF500001≤n,k≤109,L∗F≤50000

解题思路:

xi,jxi,j表示第jj轮扔到i的概率,那么:

ans=E((x1,1++x1,n)F...(xL,1+...+xL,n)F)ans=E((x1,1+…+x1,n)F∗...∗(xL,1+...+xL,n)F)

我们知道单个xi,j=1kxi,j=1k,而且和的期望等于期望的和,但只有在各个事件独立时积的期望才等于期望的积,而式子中xi,axi,axj,axj,a显然不独立,所以不能直接算。

考虑将式子拆开,即

ans=E(ΠLi=1ΠFj=1xi,Ai,j)Ai,j[1,n]ans=∑E(Πi=1LΠj=1Fxi,Ai,j),Ai,j∈[1,n]

注意到如果存在ij,Ai,a=Aj,bi≠j,Ai,a=Aj,b,即第Ai,aAi,a轮既扔到ii,又扔到j,这显然是不可能的,所以该项值为0;否则记所有Ai,jAi,j中出现的不同值个数为tt,那么这t个事件独立,该项值为1kt1kt

考虑枚举在[1,LF][1,L∗F]枚举tt,计算不存在ij,Ai,a=Aj,b的方案数,那么

ans=t=1LF(nt)t!ktG(t)ans=∑t=1L∗F(nt)t!ktG(t)

G(t)G(t)就表示已经选出了tt种值且第一次出现顺序下分配给LFAi,jAi,j的方案数。

用更直观的语言描述:有LL个有编号的盒子,每个盒子有FF个有编号的格子放球,LFL∗F 个球的编号要在[1,n][1,n] 中,不同编号有tt种,且任意两个盒子不能放有相同编号的球,求方案数。

假设一个盒子分到i种编号,由于我们已经枚举了编号出现的位置,所以编号之间没有顺序,相当于要把FF个格子分配到i个集合中,方案数显然是第二类斯特林数SiFSFi

LL个盒子一共有t种编号,考虑生成函数g(x)=SiFxig(x)=∑SFixi,那么G(t)G(t)即为g(x)Lg(x)Ltt次项系数,记为g(x)L[xt]

所以

ans=t=1LF(nt)t!ktg(x)L[xt]=t=1LFn!(nt)!ktg(x)L[xt]ans=∑t=1L∗F(nt)t!ktg(x)L[xt]=∑t=1L∗Fn!(n−t)!ktg(x)L[xt]

注意到一旦t2003t≥2003n!(nt)!n!(n−t)!就一定为0,所以只用考虑2003项即可。

预处理斯特林数,暴力做多项式乘法,时间复杂度为O(2003F+20032logL)O(2003F+20032logL)

#include<bits/stdc++.h>
using namespace std;
const int N=50005,mod=2003;
int n,K,L,F,s[N][2500];
int Pow(int x,int y)
{
    int res=1;x%=mod;
    for(;y;y>>=1,x=x*x%mod)
        if(y&1)res=res*x%mod;
    return res;
}
struct Poly
{
    int deg,a[N];
    Poly(){deg=0;memset(a,0,sizeof(a));}
    inline friend Poly mul(const Poly &A,const Poly &B)
    {
        Poly res;res.deg=min(mod,A.deg+B.deg);
        for(int i=0;i<=A.deg;i++)
            for(int j=0;j<=B.deg&&i+j<=mod;j++)
                res.a[i+j]=(res.a[i+j]+A.a[i]*B.a[j])%mod;
        return res;
    }
    inline friend Poly Pow(Poly A,int b)
    {
        Poly res;res.a[0]=1;
        for(;b;b>>=1,A=mul(A,A))
            if(b&1)res=mul(res,A);
        return res;
    }
}G;
void pre()
{
    for(int i=1;i<=F;i++)
    {
        s[i][1]=1;
        for(int j=2;j<=min(i,mod);j++)
            s[i][j]=(s[i-1][j-1]+s[i-1][j]*j)%mod;
    }
    G.deg=F;
    for(int i=1;i<=mod;i++)G.a[i]=s[F][i];
    G=Pow(G,L);
}
int main()
{
    freopen("vodka.in","r",stdin);
    freopen("vodka.out","w",stdout);
    scanf("%d%d%d%d",&n,&K,&L,&F);n%=mod,K%=mod;
    pre();
    int inv=Pow(K,mod-2),num=1,ans=0;
    for(int i=1;i<=L*F&&num;i++)
    {
        num=num*(n-i+1)%mod*inv%mod;
        ans=(ans+num*G.a[i])%mod;
    }
    cout<<ans<<'\n';
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值