【題目】Good Bye 2017 D. New Year and Arbitrary Arrangement算法
【題意】給定正整數k,pa,pb,初始有空字符串,每次有pa/(pa+pb)的可能在字符串末尾+a,有pb/(pa+pb)的可能在字符串末尾+b,求加到組成至少k對子序列「ab"時的指望子序列「ab」數。k<=1000,pa,pb<=10^6。ide
【算法】指望DPurl
【題解】主要問題在於字符串無限延伸,那麼須要考慮記錄前綴的關鍵量來爲DP設置終止狀態。spa
設f[i][j]表示前綴中有i個a和j個ab中止後的指望長度,A=pa/(pa+pb),B=pb/(pa+pb)。.net
狀態轉移方程:f[i][j]=A*f[i+1][j]+B*f[i][i+j]code
接下來解決兩個問題:blog
1.終止狀態:當i+j>=k時,再加一個b就會終止,指望爲i+j+c,其中:字符串
c=0*B+1*A*B+2*A^2*B+...+∞*A^∞*Bget
等差*等比數列,運用高中數學的錯位相減法(特別的,A^∞=0),能夠獲得:數學
c=pa/pb
故有終止狀態f[i][j]=i+j+pa/pb,i+j>=k。
2.初始狀態:初始空字符串爲f[0][0],可是會發現f[0][0]會從f[0][0]自己轉移,其緣由是沒有a時會無限加b,解決辦法是初始狀態設爲f[1][0]。
#include<cstdio> const int m=1e9+7,N=1010; void gcd(int a,int b,int&x,int &y){ if(!b){x=1;y=0;} else{gcd(b,a%b,y,x);y-=x*(a/b);} } int inv(int a){int x,y;gcd(a,m,x,y);return (x%m+m)%m;} int f[N][N],k,pa,pb,A,B,C; int main(){ scanf("%d%d%d",&k,&pa,&pb); A=1ll*pa*inv(pa+pb)%m;B=(1-A+m)%m;C=1ll*pa*inv(pb)%m; for(int i=k;i>=1;i--)for(int j=k;j>=0;j--) f[i][j]=i+j>=k?(i+j+C)%m:(1ll*A*f[i+1][j]+1ll*B*f[i][i+j])%m; printf("%d",f[1][0]); return 0; }