CF1278F Cards
Link
Description
有 \(m\) 张牌,其中有一张是王牌。将这些牌均匀随机打乱 \(n\) 次,设有 \(x\) 次第一张为王牌,求 \(x^k\) 的期望值。
答案对 \(998244353\) 取模。
Solution
容易知道一次随机后第一张牌为王牌的概率为
那么 \(X^K\) 的期望就为
此时,通过枚举 \(n\) 我们得到一个 \(O(n)\) 的算法,这可以做 \(n\leq K\) 的情况,但是显然不够优秀。对于 \(n>K\) 的情况,考虑化简式子,根据套路把后面的 \(i^K\) 拆了。
复杂度 \(O(K^2)\)
Link
Description
加强版,\(n,m\leq 998244352,K\leq 10^7\)
Solution
依赖于 \(K^2\) 的算法不幸挂掉了,数据范围显然是想让我们线性递推。
这个式子到底慢在哪儿?瓶颈在于求斯特林数,而求斯特林数最快的方法莫过于卷积,然而这也是 \(O(K \log K)\) 的。这就提示我们要把斯特林数给化掉。套用
组合数有什么优美性质呢?它可以拆成两项小的组合从而递推,在遇到复杂的求和式子时可以考虑这一方法;它可以快速邻项转移,只要标号是连续的,就能快速转移,套用 \(\binom{n}{i}=\frac{n-i+1}{i}\binom{n}{i-1}\)。运用这两个性质,就能做很多事了。
记 \(f_x=\sum_{j=0}^x \binom{n-K+x}{j}(-p)^j\)
前两项都可以通过邻项直接转移,\(i^K\) 是积性函数,可以直接线性筛,现在只需要把 \(f_x\) 快速算出来即可。于是又要用到组合数优美的性质,套用
来得到 \(f_x\) 的递推式,记 \(C=n-K\)。
注意到 \(\binom{C+x}{x+1}\) 的下标是连续的,所以可以邻项递推,那么 \(f_x\) 也可以递推了。
复杂度 \(O(K)\)
#include<stdio.h>
#define N 10000007
#define ll long long
#define Mod 998244353
ll qpow(ll x,ll y){
ll ret=1,cnt=0;
while(y>=(1ll<<cnt)){
if(y&(1ll<<cnt)) ret=ret*x%Mod;
x=x*x%Mod,cnt++;
}
return ret;
}
ll n,m,K,inc[N],f[N],i_K[N];
bool mk[N];
int pr[N],cnt=0;
int pre_work(int lim){
inc[1]=i_K[1]=1;
for(int i=2;i<=lim;i++)
inc[i]=(Mod-inc[Mod%i]*(Mod/i)%Mod)%Mod;
for(int i=2;i<=lim;i++){
if(!mk[i]){
pr[++cnt]=i;
i_K[i]=qpow(i,K);
}
for(int j=1;j<=cnt&&pr[j]*i<=lim;j++){
mk[pr[j]*i]=1;
i_K[pr[j]*i]=i_K[pr[j]]*i_K[i]%Mod;
if(i%pr[j]==0) break;
}
}
return 0;
}
int main(){
scanf("%lld%lld%lld",&n,&m,&K);
ll p=qpow(m,Mod-2),ans=0;
if(n<=K){
pre_work(n);
ll ret=qpow(1-p+Mod,n),p1=p*qpow(1-p+Mod,Mod-2)%Mod;
for(int i=1;i<=n;i++){
ret=ret*p1%Mod*(n-i+1)%Mod*inc[i]%Mod;
ans=(ans+ret*i_K[i]%Mod)%Mod;
}
printf("%lld",ans);
}else{
pre_work(K);
ll c=n-K,ans=0,ret=1; f[0]=1;
for(int i=1;i<=K;i++){
ret=ret*(Mod-p)%Mod*(c+i-1)%Mod*inc[i]%Mod;
f[i]=(f[i-1]*(1-p+Mod)%Mod+ret)%Mod;
}
ret=1;
for(int i=0;i<=K;i++){
if(i) ret=ret*p%Mod*(n-i+1)%Mod*inc[i]%Mod;
ans=(ans+ret*i_K[i]%Mod*f[K-i]%Mod)%Mod;
}
printf("%lld",ans);
/* for(int i=1;i<=K;i++)
f[i]=(f[i-1]*(1-p+Mod)%Mod+C(c+i-1,i)*qpow(Mod-p,i)%Mod)%Mod;
for(int i=0;i<=K;i++)
ans=(ans+qpow(p,i)*C(n,i)%Mod*i_K[i]%Mod*f[K-i]%Mod)%Mod;
printf("%lld",ans); */
}
}