NOIP2018模擬賽Potato 概率Dp 矩陣優化

NOIP2018模擬賽Potato

題目

在這裏插入圖片描述
在這裏插入圖片描述

分析

考慮動態規劃。
首先考慮到產生一個jj級土豆的條件是有兩個j1j-1級的土豆。
由於每次都把土豆往左移,所以每次都是後面空出來一塊盒子讓你放。
於是可以Dp,先求當前一共有ii個盒子,產生了一個jj級土豆的概率。(接下來都假設有ii個盒子)
a[i][j]=a[i1][j1]a[i][j1]a[i][j]=a[i-1][j-1]\cdot a[i][j-1]
考慮產生一個jj級土豆,並且之後不再產生任何高於jj級土豆的概率。
A[i][j]=a[i][j](1a[i1][j])A[i][j]=a[i][j]\cdot(1-a[i-1][j])
因爲如果連一個jj級土豆都沒有產生的話,一定不會有更高級的土豆出現。
在此基礎上,求最終第ii個位置上是jj級土豆,[i,i+n][i,i+n]內的所有土豆等級和的期望。
f[i][j]=j+Ek=1j1(f[i+1][k],A[ni][k])f[i][j]=j+E_{k=1}^{j-1}(f[i+1][k], A[n - i][k])
其中EE表示的是加權平均,也就是
E(xi,wi)=xiwiwiE(x_i,w_i)=\frac{\sum x_i\cdot w_i}{\sum w_i}
也就是考慮i+1i+1個位置上最終爲kk級別的土豆的期望:先產生一個kk級別的土豆,之後都不在產生kk級別的土豆,在此基礎上乘上這個情況下之後所有土豆等級和的期望。
但是要特殊考慮一種情況,就是j=1j=1,這種情況下i+1i+1位置只能直接產生一個22級的土豆。
所以額外新建狀態,b[i][j]b[i][j]表示最開始放了一個22級的土豆,合成了jj的土豆的概率,B[i][j]B[i][j]表示之後都不再產生高於jj級別的土豆。轉移方程:
b[i][j]=b[i][j1](1a[i1][j1])b[i][j]=b[i][j-1]\cdot(1-a[i-1][j-1])
B[i][j]=b[i][j](1a[i1][j])B[i][j]=b[i][j]\cdot (1-a[i-1][j])
f[i][1]=Ek=2inf(f[i+1][k],B[ni][k])f[i][1]=E_{k=2}^{inf}(f[i+1][k],B[n-i][k])
最終的答案是Ans=k=1inf(f[1][k]A[n][k])Ans=\sum_{k=1}^{inf}(f[1][k]\cdot A[n][k])
轉移的複雜度是O(n3)O(n^3)的,只能通過Subtask4Subtask4
然而不難發現,土豆等級越高產生的概率應該是指數級別下降的。所以考慮捨去大於某個等級的土豆(標解捨去的是50級以上的土豆)
這樣的話,當n50n\ge 50的時候i+1>ii+1->i的轉移方程是一模一樣的,採用矩陣優化即可。
複雜度O(503logn)O(50^3logn)

代碼

#include<bits/stdc++.h>
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
double a[52][52], b[52][52], s[52][52], f[52][52];
struct Maxtir {
	double m[52][52];
	Maxtir() {memset(m, 0, sizeof(m));}
	double *operator[](int i) {return m[i];}
	Maxtir operator * (Maxtir b) {
		Maxtir c;
		for(int i = 1;i <= 51; ++i)
			for(int j = 1;j <= 51; ++j)
				for(int k = 1;k <= 51; ++k)
					c[i][j] += m[i][k] * b[k][j];
		return c;
	}
}A, B;
void Pow(int k) {B = A; for(;k; A = A * A, k >>= 1) if(k & 1) B = B * A;}
int main() {
	freopen("potato.in","r",stdin);
	freopen("potato.out","w",stdout);
	int n = ri(); double r = 0, p1 = ri() / 1e9, p2 = 1 - p1;
	for(int i = 1;i <= 50; ++i)
		for(int j = 1;j <= 50; ++j) {
			if(j == 1) a[i][j] += p1;
			if(j == 2) a[i][j] += p2, b[i][j] += p2;
			a[i][j] += a[i][j - 1] * a[i - 1][j - 1];
			b[i][j] += b[i][j - 1] * a[i - 1][j - 1];
		}
	for(int i = 50; i; --i)
		for(int j = 1;j <= 50; ++j)
			a[i][j] *= (1 - a[i - 1][j]), b[i][j] *= (1 - a[i - 1][j]);
	for(int i = 1;i <= 50; ++i) f[50][i] = i;
	for(int i = 49; ~i; --i) {
		for(int k = 2;k <= 50; ++k) 
			f[i][1] += f[i + 1][k] * b[50 - i][k], s[i][1] += b[50 - i][k];
		for(int j = 2;j <= 50; ++j) 
			for(int k = 1;k < j; ++k)
				f[i][j] += f[i + 1][k] * a[50 - i][k], s[i][j] += a[50 - i][k];
		for(int j = 1;j <= 50; ++j) 
			(f[i][j] /= s[i][j]) += j;
	}
	if(n <= 51) {
		for(int i = 1;i <= 50; ++i) 
			r += a[n][i] * f[51 - n][i];
		return printf("%.10lf\n", r), 0;
	}
	A[1][51] = A[51][51] = 1;
	for(int k = 2;k <= 50; ++k) A[1][k] += b[50][k] / s[0][1];
	for(int j = 2;j <= 50; A[j][51] = j, ++j) 
		for(int k = 1;k < j; ++k) 
			A[j][k] += a[50][k] / s[0][j];
	Pow(n - 52);
	for(int i = 1;i <= 50; ++i) {
		double s = 0;
		for(int j = 1;j <= 50; ++j)
			s += B[i][j] * f[0][j];
		s += B[i][51];
		r += s * a[50][i];
	}
	printf("%.10lf\n", r);
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章