题目描述:
AlanAlan在玩骰子游戏,AlanAlan会玩nn轮骰子,每轮的数值在[1,K][1,K]中随机出现。记aiai表示nn轮投掷中,数值ii出现的次数,求aF1∗aF2∗……aFLa1F∗a2F∗……aLF的期望。答案对2003取模。
1≤n,k≤109,L∗F≤500001≤n,k≤109,L∗F≤50000
解题思路:
设xi,jxi,j表示第jj轮扔到的概率,那么:
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,a和xj,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]
注意到如果存在i≠j,Ai,a=Aj,bi≠j,Ai,a=Aj,b,即第Ai,aAi,a轮既扔到ii,又扔到,这显然是不可能的,所以该项值为0;否则记所有Ai,jAi,j中出现的不同值个数为tt,那么这个事件独立,该项值为1kt1kt。
考虑枚举在[1,L∗F][1,L∗F]枚举tt,计算不存在的方案数,那么
ans=∑t=1L∗F(nt)t!ktG(t)ans=∑t=1L∗F(nt)t!ktG(t)
G(t)G(t)就表示已经选出了tt种值且第一次出现顺序下分配给个Ai,jAi,j的方案数。
用更直观的语言描述:有LL个有编号的盒子,每个盒子有FF个有编号的格子放球,L∗FL∗F 个球的编号要在[1,n][1,n] 中,不同编号有tt种,且任意两个盒子不能放有相同编号的球,求方案数。
假设一个盒子分到种编号,由于我们已经枚举了编号出现的位置,所以编号之间没有顺序,相当于要把FF个格子分配到个集合中,方案数显然是第二类斯特林数SiFSFi 。
LL个盒子一共有种编号,考虑生成函数g(x)=∑SiFxig(x)=∑SFixi,那么G(t)G(t)即为g(x)Lg(x)L中tt次项系数,记为。
所以
ans=∑t=1L∗F(nt)t!ktg(x)L[xt]=∑t=1L∗Fn!(n−t)!ktg(x)L[xt]ans=∑t=1L∗F(nt)t!ktg(x)L[xt]=∑t=1L∗Fn!(n−t)!ktg(x)L[xt]
注意到一旦t≥2003t≥2003,n!(n−t)!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&#i++)
{
num=num*(n-i+1)%mod*inv%mod;
ans=(ans+num*G.a[i])%mod;
}
cout<<ans<<'\n';
return 0;
}