【BZOJ2510】弱題 期望DP+循環矩陣乘法
阿新 • • 發佈:2017-07-07
mem ret sam include std bsp 都是 size 個數
應包含N行,第i行為標號為i的球的期望個數,四舍五入保留3位小數。
3 0
1.333
對於10%的數據,N ≤ 5, M ≤ 5, K ≤ 10;
對於20%的數據,N ≤ 20, M ≤ 50, K ≤ 20;
對於30%的數據,N ≤ 100, M ≤ 100, K ≤ 100;
對於40%的數據,M ≤ 1000, K ≤ 1000;
對於100%的數據,N ≤ 1000, M ≤ 100,000,000, K ≤ 2,147,483,647。
【BZOJ2510】弱題
Description
有M個球,一開始每個球均有一個初始標號,標號範圍為1~N且為整數,標號為i的球有ai個,並保證Σai = M。 每次操作等概率取出一個球(即取出每個球的概率均為1/M),若這個球標號為k(k < N),則將它重新標號為k + 1;若這個球標號為N,則將其重標號為1。(取出球後並不將其丟棄) 現在你需要求出,經過K次這樣的操作後,每個標號的球的期望個數。Input
第1行包含三個正整數N,M,K,表示了標號與球的個數以及操作次數。 第2行包含N個非負整數ai,表示初始標號為i的球有ai個。Output
Sample Input
2 3 23 0
Sample Output
1.6671.333
HINT
【樣例說明】
第1次操作後,由於標號為2球個數為0,所以必然是一個標號為1的球變為標號為2的球。所以有2個標號為1的球,有1個標號為2的球。
第2次操作後,有1/3的概率標號為2的球變為標號為1的球(此時標號為1的球有3個),有2/3的概率標號為1的球變為標號為2的球(此時標號為1的球有1個),所以標號為1的球的期望個數為1/3*3+2/3*1 = 5/3。同理可求出標號為2的球期望個數為4/3。
【數據規模與約定】
對於20%的數據,N ≤ 20, M ≤ 50, K ≤ 20;
對於30%的數據,N ≤ 100, M ≤ 100, K ≤ 100;
對於40%的數據,M ≤ 1000, K ≤ 1000;
對於100%的數據,N ≤ 1000, M ≤ 100,000,000, K ≤ 2,147,483,647。
題解:一開始too naive,以為同樣用一個期望DP的黑科技就能過(k=min(k,5000))~
發現正解又是矩陣乘法,但是矩乘不是n^3的嗎?本題有特殊性質。
我們的DP方程長這樣:f[i][j]=f[i的上一個][j-1]/m+f[i][j-1]*(m-1)/m
所以我們的轉移矩陣的每一行都是循環相同的,將轉移矩陣自乘若幹次後,每一行仍然是循環相同的,所以我們的矩陣實際上只需要維護一行,那麽轉移一次的代價自然就是O(n^2)的。
那麽具體實現呢?其實非常簡單,直接c[i+j]+=a[i]*b[j]。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; int n,m,k; struct M { double v[1010]; M (){memset(v,0,sizeof(v));} double& operator [] (int x) {return v[x];} M operator * (M a) { M ret; for(int i=0;i<n;i++) for(int j=0;j<n;j++) ret[(i+j)%n]+=v[i]*a[j]; return ret; } }; M ans,x; void pm(int y) { while(y) { if(y&1) ans=ans*x; x=x*x,y>>=1; } } int main() { scanf("%d%d%d",&n,&m,&k); int i; for(i=0;i<n;i++) scanf("%lf",&ans[i]); x[0]=(double)(m-1)/m,x[1]=(double)1/m; pm(k); for(i=0;i<n;i++) printf("%.3lf\n",ans[i]); return 0; }
【BZOJ2510】弱題 期望DP+循環矩陣乘法