牛客第9場-E 概率dp
阿新 • • 發佈:2019-01-24
把概率dp當數學題做推公式,也是我太菜了。最重要是我還津津有味推了一下午。
剛一開始我想到列舉對的題數,從n到0跑一遍,預處理出來全部發生的概率pp。然後通過
當 對的題數為n時 : 序列全部連續 得分為 pp * n^m.
當對的題數為n-1時: 我們要判斷錯的那個題在什麼位置 得分為累積和: 令i從1到n, pp*(100-p[i])/p[i] * [(i-1)^m+(n-i)^m]
當對的題數為n-2時: 類比得分為 令i從1到n,j從i+1到n, pp*(100-p[i])/p[i]*(100-p[j])/p[j] *[(i-1)^m+(j-1-i)^m+(n-j)^m]
.................
當對的題為0時 得分 也為0。
後來仔細想想這不就是把所有的情況都算了一遍嗎,類似於概率論求期望。當n為1000時,時間複雜度為(2^n),這是萬萬不可以的。看到大家用時不多,所以我又想到這可能是個規律題。又瘋狂開始找遞推公式,一找就是一下午。
步入正題:
首先我們要把100的逆元處理出來,畢竟題意中給的給的是p[i],而這件事發生的概率為p[i]/100.
然後我們列舉到每個題的位置時它的得分。不斷用一層迴圈遍歷這個位置之前的狀態。把每一種情況都考慮進去。
程式碼如下:
#include<bits/stdc++.h> #define maxn 1005 #define mod 1000000007 typedef long long ll; using namespace std; ll p[maxn],f[maxn]; ll _pow(ll a,ll b){ ll ans=1; while(b){ if(b&1) ans=ans*a%mod; a=a*a%mod; b=b>>1; } return ans%mod; } int main(){ int n,m; ll inv = _pow(100,mod-2); scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) scanf("%lld",&p[i]); for(int i=1;i<=n;i++) { f[i]=f[i-1]*(100-p[i])%mod; f[i]=f[i]*inv%mod; ll pb=1,sum; for(int j=1;j<=i;j++) { sum=j; pb=pb*p[i-j+1]%mod; pb=pb*inv%mod; ll tmp = pb*(100-p[i-j])%mod; tmp=tmp*inv%mod; ll a; if(i==j) a=0; else a=f[i-j-1]; f[i]=(f[i]+tmp*(_pow(sum,m)+a)%mod)%mod; } } printf("%d\n",f[n]); return 0; }
題目