1. 程式人生 > >時機成熟之時

時機成熟之時

print end 範圍 splay 註意 endif 細節 inline ont

Description

  總共有\(n\)種物品,每秒等概率拿到其中一種,第\(i\)秒要花費\(i^k\)的費用,問集齊\(n\)種物品花費的期望值,對\(998244353\)取模

  數據範圍:\(1\leq n,k\leq 100\)

  

Solution

?  *為了避免混淆,以下用大寫\(K\)表示題目中的\(k\)

?  一個前置技能:\((x+1)^k=\sum\limits_{i}\binom m 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]=\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\)

?  所以這個時候我們可以將原來的\(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\),所以其實就是相當於上面那個假轉移式加上\(2^j\)

  然後把右邊的\(f[i][k](k=j)\)這項移到左邊去搞一搞就好了

?  小細節:因為當\(i=n\)的時候\(n-i=0\),然後就會出現在移完項之後的式子裏面分母為\(0\)的情況,所以這條轉移是不能計算\(f[n]\)的,因為一旦集齊就不需要再進行操作,所以答案直接就是\(f[n-1][K]+1\)

  時間復雜度\(O(n^3)\)(然而因為懶裏面還套了快速冪的一個\(log\),不過\(n\)比較小所以就。。這樣吧233333)

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);
}
void solve(){
    int tmp1,tmp2,p1,p2,invn=ksm(n,MOD-2);
    int tmp;
    f[1][0]=mul(n,ksm(n-1,MOD-2));
    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,invn); p2=plu(1,MOD-p1);
            f[i][j]=plu(mul(p1,tmp1),plu(mul(p2,tmp2),ksm(2,j)));
            tmp=mul(n,ksm(n-i,MOD-2));
            f[i][j]=mul(f[i][j],tmp);
        }
    printf("%d\n",plu(f[n-1][K],1));
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    scanf("%d%d",&n,&K);
    prework(max(n,K));
    solve();
}

時機成熟之時