csp模擬27-金箱子(題解)

houbur發表於2024-08-22

題目連結(顯然還沒有找到原題)

雖說我現在才學會期望dp顯得不太好,但沒辦法,誰讓我比較菜~~,之前模擬賽已經考過幾道類似的題了,但都一筆帶過了,這次算是正式學習了一下這類題,於是就有了這篇題解。

首先看到k次方首先想到的就是我們在進行dp轉移的時候不太方便,這個時候很自然的想到二項式定理去避免機率的重複計算,可以把k次冥的轉移轉換成for迴圈來做,這個時候就要去考慮轉移方程了:設\(f[i]\)表示前i個寶箱的期望貢獻,則顯然有

\[f[i]^j=(f[i-1]+a[i])^j*p[i]+(f[i-1]+b[i])^j*(1-p[i]) \]

二項式定理展開即為:

\[f[i]^j=(\sum _{k=0}^{j} C _{j}^{k}f[i-1]^ka[i]^{j-k})*p[i]+(\sum _{k=0}^{j} C _{j}^{k}f[i-1]^kb[i]^{j-k})*(1-p[i]) \]

可以顯然觀察到兩個式子不同的只有\((a[i]^{j-k}*p[i]/b[i]^{j-k}(1-p[i]))\),那麼即可合併得:

\[f[i]^j=(\sum _{k=0}^{j} C _{j}^{k}f[i-1]^k(a[i]^{j-k}*p[i]+b[i]^{j-k}*(1-p[i]))) \]

此時dp轉移中的\(f[i]^j\)並不是很好處理,考慮將dp陣列多開一維,令\(f[i][j]\)表示\(f[i]\)在j次方下的取值並可以令\(op[i][j]=a[i]^j*p[i]+b[i]^j*(1-p[i])\),就可以很好的處理dp轉移中的各種狀態,最終完全版的轉移就是:

\[f[i][j]=(\sum _{k=0}^{j} C _{j}^{k}f[i-1][k]op[i][j-k])) \]

總時間複雜度為\(O(nk^2)\),壓線透過此題。
最後附上程式碼:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e4+5,mod=998244353;
int n,m,l,r,a[N],b[N],op[N][101],p[N][2];
int x,y,z,t,k,ans,f[N][101],c[101][101];
int quickpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}return ans;
}
signed main(){
	freopen("in.in","r",stdin);
	freopen("out.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>p[i][0]>>a[i]>>b[i];
		p[i][1]=(mod+1-p[i][0])%mod;
	}
	c[1][0]=c[1][1]=c[0][0]=1;
	for(int i=2;i<=k;i++){
		c[i][1]=i;c[i][0]=1;
		for(int j=2;j<=i;j++){
			c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=k;j++){
			op[i][j]=(quickpow(a[i],j)*p[i][0]%mod+quickpow(b[i],j)*p[i][1]%mod)%mod;
		}f[i][0]=1;op[i][0]=1;
	}
	for(int i=1;i<=k;i++)f[1][i]=op[1][i];
	for(int i=2;i<=n;i++){
		for(int j=1;j<=k;j++){
			for(int kk=0;kk<=j;kk++){
				f[i][j]=(f[i][j]+c[j][kk]*f[i-1][kk]%mod*op[i][j-kk]%mod)%mod;
			}
		}
	}cout<<f[n][k];
	return 0;
}

結束嘍~~~~~