总共有\(n\)种物品,每秒等几率拿到其中一种,第\(i\)秒要花费\(i^k\)的费用,问集齐\(n\)种物品花费的指望值,对\(998244353\)取模(本题内约定\(0^0=1\))ios
数据范围:\(1\leq n,k\leq 100\)c++
*为了不混淆,如下用大写\(K\)表示题目中的\(k\)spa
一个前置技能:\((x+1)^k=\sum\limits_{i}\binom k i x^i\)code
而后考虑dp,设\(f[i][j]\)表示集齐\(i\)种物品的、计算花费的指数为\(j\)的指望花费,那么多拿一个物品有两种不一样的状况:(1)拿到一个已经拿过的物品,几率为\(\frac{i}{n}\);(2)拿到一个新物品,几率为\((1-\frac{i}{n}\))ip
而后咱们还要考虑上多拿的这个物品带来的花费string
然而如今的问题是咱们并不知道转移过来的状态里面已通过去了多少秒,因此没法直接得出花费it
(如下是感性理解时间qwq不必定对。。)io
这个时候考虑一下\(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\)class
那么新加了一个物品以后咱们想要获得的应该是\(P_1\cdot(1^k+2^k)+P_2\cdot(1^k+2^k+3^k)+...\)这样,这个时候就能够用前置技能的那条式子了:
\[ f'[i][j]=\frac{i}{n}\sum\limits_{k}\binom j k f[i][k]+(1-\frac{i}{n})\sum\limits_{k}\binom j k f[i-1][k] \]
然而实际上这个获得的并非真正的\(f[i][j]\),由于注意到用上面那条式子偏移以后咱们获得的是\(P_1\cdot (2^k)+P_2\cdot (2^k+3^k)+...\)这样的东西,每项里面都少了一个\(1^k\)stream
因此这个时候咱们能够将原来的\(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\)了)
因此真正的转移应该是:
\[ f[i][j]=\frac{i}{n}\sum\limits_{k}\binom j k (f[i][k]+0^k)+(1-\frac{i}{n})\sum\limits_{k}\binom j k (f[i-1][k]+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)\)
#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(); }