总共有(n)种物品,每秒等概率拿到其中一种,第(i)秒要花费(i^k)的费用,问集齐(n)种物品花费的期望值,对(998244353)取模(本题内约定(0^0=1))
数据范围:(1leq n,kleq 100)
Solution
*为了避免混淆,以下用大写(K)表示题目中的(k)
一个前置技能:((x+1)^k=sumlimits_{i}inom k i x^i)
然后考虑dp,设(f[i][j])表示集齐(i)种物品的、计算花费的指数为(j)的期望花费,那么多拿一个物品有两种不同的情况:(1)拿到一个已经拿过的物品,概率为(frac{i}{n});(2)拿到一个新物品,概率为((1-frac{i}{n}))
然后我们还要考虑上多拿的这个物品带来的花费
然而现在的问题是我们并不知道转移过来的状态里面已经过去了多少秒,所以无法直接得出花费
(以下是感性理解时间qwq不一定对。。)
这个时候考虑一下(f)本身的含义,因为它是期望,也就是形如(P_1cdot (1^k)+P_2cdot(1^k+2^k)+P_3cdot(1^k+2^k+3^k)+...+P_mcdot(1^k+2^k+3^k+...+m^k)),其中(sumlimits_{i} P_i=1)
那么新加了一个物品之后我们想要得到的应该是(P_1cdot(1^k+2^k)+P_2cdot(1^k+2^k+3^k)+...)这样,这个时候就可以用前置技能的那条式子了:
然而实际上这个得到的并不是真正的(f[i][j]),因为注意到用上面那条式子偏移之后我们得到的是(P_1cdot (2^k)+P_2cdot (2^k+3^k)+...)这样的东西,每项里面都少了一个(1^k)
所以这个时候我们可以将原来的(P_1cdot (1^k)+P_2cdot(1^k+2^k)+...)每项加上一个(0^k)得到(P_1cdot (0^k+1^k)+P_2cdot (0^k+1^k+2^k)+...)这样的式子,然后再进行偏移就是我们要的形式了,而这样操作其实就是相当于在转移的时候给(f[i][k])和(f[i-1][k])分别加上一个(0^k)(因为(sum P_i=1)提出来就是(0^k)了)
所以真正的转移应该是:
因为(k)是从(0)开始枚举的,(0^0=1),其他都是(0),所以其实就是相当于上面那个假转移式加上(1)
然后把右边的(f[i][k](k=j))这项移到左边去搞一搞就好了
小细节:因为当(i=n)的时候(n-i=0),然后就会出现在移完项之后的式子里面分母为(0)的情况,所以这条转移是不能计算(f[n])的,因为一旦集齐就不需要再进行操作,所以(f[n][K])只能从(f[n-1][K])转移过来,所以就是同样的用上面的式子偏移一下得到(sumlimits_{i}inom K i(f[n-1][i]+0^i))就是答案了
时间复杂度(O(n^3))
Code
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=110,MOD=998244353;
int fac[N],invfac[N],inv[N];
int f[N][N];
int n,K;
int mul(int x,int y){return 1LL*x*y%MOD;}
int plu(int x,int y){return (1LL*x+y)-(1LL*x+y>=MOD?MOD:0);}
int C(int n,int m){return n<m?0:mul(fac[n],mul(invfac[m],invfac[n-m]));}
int ksm(int x,int y){
int ret=1,base=x;
for (;y;y>>=1,base=mul(base,base))
if (y&1) ret=mul(ret,base);
return ret;
}
void prework(int n){
fac[0]=1;
for (int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
invfac[n]=ksm(fac[n],MOD-2);
for (int i=n-1;i>=0;--i) invfac[i]=mul(invfac[i+1],i+1);
inv[0]=inv[1]=1;
for (int i=2;i<=n;++i) inv[i]=mul(MOD-MOD/i,inv[MOD%i]);
}
void solve(){
int tmp1,tmp2,p1,p2;
int tmp;
f[1][0]=mul(n,inv[n-1]);
for (int i=1;i<n;++i)
for (int j=0;j<=K;++j){
if (i==1&&j==0) continue;
tmp1=tmp2=0;
for (int k=0;k<=j;++k){
if (k<j)
tmp1=plu(tmp1,mul(C(j,k),f[i][k]));
tmp2=plu(tmp2,mul(C(j,k),f[i-1][k]));
}
p1=mul(i,inv[n]); p2=plu(1,MOD-p1);
f[i][j]=plu(mul(p1,tmp1),plu(mul(p2,tmp2),1));
tmp=mul(n,inv[n-i]);
f[i][j]=mul(f[i][j],tmp);
}
int ans=0;
for (int i=0;i<=K;++i)
ans=plu(ans,mul(C(K,i),f[n-1][i]));
ans=plu(ans,1);
printf("%d
",ans);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d%d",&n,&K);
prework(max(n,K));
solve();
}