總共有\(n\)種物品,每秒等概率拿到其中一種,第\(i\)秒要花費\(i^k\)的費用,問集齊\(n\)種物品花費的期望值,對\(998244353\)取模(本題內約定\(0^0=1\))
數據范圍:\(1\leq n,k\leq 100\)
Solution
*為了避免混淆,以下用大寫\(K\)表示題目中的\(k\)
一個前置技能:\((x+1)^k=\sum\limits_{i}\binom k i x^i\)
然后考慮dp,設\(f[i][j]\)表示集齊\(i\)種物品的、計算花費的指數為\(j\)的期望花費,那么多拿一個物品有兩種不同的情況:(1)拿到一個已經拿過的物品,概率為\(\frac{i}{n}\);(2)拿到一個新物品,概率為\((1-\frac{i}{n}\))
然后我們還要考慮上多拿的這個物品帶來的花費
然而現在的問題是我們並不知道轉移過來的狀態里面已經過去了多少秒,所以無法直接得出花費
(以下是感性理解時間qwq不一定對。。)
這個時候考慮一下\(f\)本身的含義,因為它是期望,也就是形如\(P_1\cdot (1^k)+P_2\cdot(1^k+2^k)+P_3\cdot(1^k+2^k+3^k)+...+P_m\cdot(1^k+2^k+3^k+...+m^k)\),其中\(\sum\limits_{i} P_i=1\)
那么新加了一個物品之后我們想要得到的應該是\(P_1\cdot(1^k+2^k)+P_2\cdot(1^k+2^k+3^k)+...\)這樣,這個時候就可以用前置技能的那條式子了:
然而實際上這個得到的並不是真正的\(f[i][j]\),因為注意到用上面那條式子偏移之后我們得到的是\(P_1\cdot (2^k)+P_2\cdot (2^k+3^k)+...\)這樣的東西,每項里面都少了一個\(1^k\)
所以這個時候我們可以將原來的\(P_1\cdot (1^k)+P_2\cdot(1^k+2^k)+...\)每項加上一個\(0^k\)得到\(P_1\cdot (0^k+1^k)+P_2\cdot (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]\)轉移過來,所以就是同樣的用上面的式子偏移一下得到\(\sum\limits_{i}\binom 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\n",ans);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d%d",&n,&K);
prework(max(n,K));
solve();
}