@xiaoziyao
2021-04-03T15:09:47.000000Z
字数 3497
阅读 1087
解题报告
张牌,有一张是王牌,打乱次,设有次第一次为王牌,求的期望。()
死亡推式子题。。。刚好可以总结一些推式子题套路。
设,那么首先很容易列出式子:
枚举王牌出现次数,然后不难得到这个式子。
但这个和式的上界是,怎么优化枚举都没有办法,所以套路地用第二类斯特林数展开,然后交换求和式(组合意义就是个球放到个盒子里):
利用这个式子:
然后利用一个结论(下方有说明)继续推:
这个结论很常见:
利用上面提到的第二类斯特林数通项暴力展开,再次交换求和式:
对上面展开幂次的式子二项式反演得:
设,那么原式就变成可以递推的式子了:
那么我们现在就要求出,很容易知道时。
考虑倒序递推,直接暴力展开组合数:
那么我们随便筛一下,然后拆一下组合数,就可以线性递推出来了。
谨慎起见,可以特判一下的情况。
#include<stdio.h>
const int maxk=10000005,mod=998244353;
int n,m,k,p,cnt,ans,mul;
int pw[maxk],c[maxk],P[maxk],inv[maxk],S[maxk],pmul[maxk],npmul[maxk],fmul[2];
int ksm(int a,int b){
int res=1;
while(b){
if(b&1)
res=1ll*res*a%mod;
a=1ll*a*a%mod,b>>=1;
}
return res;
}
void sieve(int n){
fmul[0]=1,fmul[1]=mod-1;
inv[1]=c[1]=pw[1]=1;
for(int i=2;i<=n;i++){
inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
if(c[i]==0)
P[++cnt]=i,pw[i]=ksm(i,k);
for(int j=1;j<=cnt;j++){
if(i*P[j]>n)
break;
c[i*P[j]]=1,pw[i*P[j]]=1ll*pw[i]*pw[P[j]]%mod;
if(i%P[j]==0)
break;
}
}
pmul[0]=npmul[0]=1;
for(int i=1;i<=n;i++)
pmul[i]=1ll*pmul[i-1]*p%mod,npmul[i]=1ll*npmul[i-1]*(1-p+mod)%mod;
}
int main(){
scanf("%d%d%d",&n,&m,&k),p=ksm(m,mod-2);
sieve(k);
if(n<k){
mul=1;//C(n,0)
for(int i=0;i<=n;i++){
ans=(ans+1ll*mul*pmul[i]%mod*npmul[n-i]%mod*pw[i]%mod)%mod;
mul=1ll*mul*inv[i+1]%mod*(n-i)%mod;//C(n,i)->C(n,i+1)
}
printf("%d\n",ans);
return 0;
}
S[k]=1;
mul=n-k;//C(n-k,1)
for(int i=k-1;i>=0;i--){
S[i]=(1ll*(1-p+mod)*S[i+1]%mod+1ll*mul*fmul[(k-i)&1]%mod*pmul[k-i]%mod)%mod;
mul=1ll*mul*inv[k-i+1]%mod*(n-i)%mod;//C(n-i-1,k-i)->C(n-i,k-i+1)
}
mul=1;//C(n,0)
for(int i=0;i<=k;i++){
ans=(ans+1ll*pmul[i]*pw[i]%mod*mul%mod*S[i]%mod)%mod;
mul=1ll*mul*inv[i+1]%mod*(n-i)%mod;//C(n,i)-C(n,i+1)
}
printf("%d\n",ans);
return 0;
}