CF1278F 解题报告
CF1278F 题解
题目描述
有 \(m\) 张牌,其中一张为王牌。现执行以下操作 \(n\) 次:均匀随机洗牌后查看第一张牌是什么。
令 \(x\) 为洗牌后第一张牌为王牌的次数,求 \(x^k\) 的期望值。
题目分析
根据期望的定义可以得到答案就是概率和权值的乘积。
设 $ P = \frac{1}{m}$,考虑枚举出现的王牌的个数 \(x\) 则答案为
直接计算的话是 \(O(n)\) 的,考虑用 \(k\) 进行递推。
展开组合数 \(\binom{n}{x}\) 可得
从 \(x^{k+1}\) 取出 \(x\) 可得
从 \(n!\) 中取出 \(n\),从 \(P^x\) 中取出一个 \(P\) 可得
因为 \(x=0\) 时不会对答案造成贡献所以 \(x-1\) 的取值为 \([0,n-1]\),考虑枚举 \(x-1\) 来代替 \(x\) 可得
用二项式定理拆解 \((x+1)^k\) 可得
更改求和顺序可得
观察式子后半段,发现即为 \(f(n-1,i)\),由此可得
由此我们得到了一个 \(O(k^3)\) 的记忆化搜索做法。终止状态为 \(f(x,0)=1\)。
继续优化,考虑每个 \(f(x,0)\) 对答案的贡献,贡献式即为
我们发现 \(n \times P \times (n-1) \times P \times \dots \times (x+1) \times P\) 只与 \(x\) 相关,这启发我们枚举 \(x\) 并计算答案。
我们记 \(g(x)=n \times P \times (n-1) \times P \times \dots \times (x+1) \times P\) 则 \(f(x,0)\) 对答案的贡献为
其中 \(x_0=k\) 并且 \(x_{n-x}\) 为 \(0\)。
考虑后面式子的组合意义就是从 \(k\) 个石子中钦定编号最小的不选择,选择剩下的部分任意多个石子,然后递归进行子问题。
考虑动态规划解决问题,设 \(dp(i,j)\) 表示考虑前 \(i\) 个数,现在在第 \(j\) 层子问题的递归中的答案。初始状态 \(dp(0,0)=1\)。
考虑两种不同转移。
第一种不进行递归,则 \(i+1\) 号石子可能属于 \(j\) 层中的任意一层。
第二种递归下一层,因为需要钦定编号最小的石子一定不能递归,所以 \(i+1\) 号石子一定只能属于 \(j+1\) 层。
时间复杂度为 \(O(k^2)\)。
代码实现
动态规划部分使用的不是题解部分的填表法,而是刷表法。为了保险,分了两个部分,\(n>k\) 与 \(n \le k\)。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
template<typename T>inline void read(T &x)
{
x=0;T f=1;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^48);
x*=f;
}
template<typename T,typename ...Arg>inline void read(T &x,Arg &...arg){read(x);read(arg...);}
template<typename T>inline void print(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar((x%10)^48);
}
template<typename T>inline void print(T x,char c){print(x);putchar(c);}
template<typename T>inline void output(T x){print(x,' ');}
template<typename T,typename ...Arg>inline void output(T x,Arg ...arg){output(x);output(arg...);}
const int N=5007,mod=998244353;
int n,m,k,P,Q,PX[N],PP[N],PQ[N],fct[N],inv[N],ans,ddd[5007][5007];
int fpow(int x,int y)
{
int res=1;
while(y)
{
if(y&1) res=(ll)res*x%mod;
x=(ll)x*x%mod; y>>=1;
}
return res;
}
int __C(int x,int y){return (ll)fct[x]*inv[y]%mod*inv[x-y]%mod;}
void prework()
{
fct[0]=PP[0]=PQ[0]=1;
for(int i=1;i<=k;i++)
PX[i]=fpow(i,k),fct[i]=(ll)fct[i-1]*i%mod,
PP[i]=(ll)PP[i-1]*P%mod,PQ[i]=(ll)PQ[i-1]*Q%mod;
inv[k]=fpow(fct[k],mod-2);
for(int i=k;i>=1;i--)
inv[i-1]=(ll)inv[i]*i%mod;
}
void solve1()
{
ans=0;
for(int x=1;x<=n;x++)
ans=(ans+(ll)__C(n,x)*PX[x]%mod*PP[x]%mod*PQ[n-x]%mod)%mod;
print(ans,'\n');
}
void solve2()
{
ddd[0][0]=1;
for(int i=0;i<k;i++)
for(int j=0;j<=k;j++)
ddd[i+1][j]=(ddd[i+1][j]+(ll)ddd[i][j]*j%mod)%mod,
ddd[i+1][j+1]=(ddd[i+1][j+1]+ddd[i][j])%mod;
int tot=1; ans=0;
for(int i=1;i<=k;i++)
tot=(ll)tot*P%mod*(n-i+1)%mod,
ans=(ans+(ll)ddd[k][i]*tot%mod)%mod;
print(ans,'\n');
}
int main()
{
read(n,m,k); P=fpow(m,mod-2),Q=(1+mod-P)%mod;
prework();
if(n<=k) solve1();
else solve2();
return 0;
}

浙公网安备 33010602011771号