CF618G Combining Slimes【条件概率DP】

题目描述:

洛谷 link
在这里插入图片描述
1 0 − 9 < p ≤ 1 10^{-9}<p\le 1 109<p1

题目分析:

先不考虑格子长度的限制,我们要得到数字 x x x 的概率 p [ x ] = p [ x − 1 ] 2 p[x]=p[x-1]^2 p[x]=p[x1]2,而 p [ 2 ] m a x = 1 − 1 0 − 9 p[2]_{max}=1-10^{-9} p[2]max=1109,那么 p [ x ] m a x = ( 1 − 1 0 − 9 ) 2 x − 1 p[x]_{max}=(1-10^{-9})^{2^{x-1}} p[x]max=(1109)2x1,当 x = 50 x=50 x=50 时已经无限趋于0,可以近似认为不可能得到大于 50 的数。

a [ i ] [ j ] a[i][j] a[i][j] 表示用 i i i 个格子得到第一个数为 j j j 的概率,有:
{ j > 2 ,   a [ i ] [ j ] = a [ i ] [ j − 1 ] ∗ a [ i − 1 ] [ j − 1 ] a [ 1 ] [ 1 ] = p ,   a [ 1 ] [ 2 ] = 1 − p i > 1 ,   a [ i ] [ 1 ] = 1 ,   a [ i ] [ 2 ] = 1 − p + p 2 \begin{cases} j>2, ~a[i][j]=a[i][j-1]*a[i-1][j-1]\\ a[1][1]=p,~a[1][2]=1-p\\ i>1,~a[i][1]=1,~a[i][2]=1-p+p^2\end{cases} j>2, a[i][j]=a[i][j1]a[i1][j1]a[1][1]=p, a[1][2]=1pi>1, a[i][1]=1, a[i][2]=1p+p2

如果相邻两个格子为 1   2 1~2 1 2,那么就不会往左合并了,可能会用到第一个产生的数为 2 形成 j j j 的概率。
b [ i ] [ j ] b[i][j] b[i][j] 表示用 i i i 个格子,第一次生成的数为2,得到数 j j j 的概率,有:
{ j > 2 ,   b [ i ] [ j ] = b [ i ] [ j − 1 ] ∗ a [ i − 1 ] [ j − 1 ] b [ i ] [ 2 ] = 1 − p \begin{cases}j>2,~b[i][j]=b[i][j-1]*a[i-1][j-1]\\ b[i][2]=1-p\end{cases} {j>2, b[i][j]=b[i][j1]a[i1][j1]b[i][2]=1p

可以发现当 i > j i>j i>j 时, a [ i + 1 ] [ j ] = a [ i ] [ j ] ,   b [ i + 1 ] [ j ] = b [ i ] [ j ] a[i+1][j]=a[i][j],~b[i+1][j]=b[i][j] a[i+1][j]=a[i][j], b[i+1][j]=b[i][j]


d p [ i ] [ j ] dp[i][j] dp[i][j] 表示在最终序列中从后往前数第 i i i 个数为 j j j 的前提下,后 i i i 个数的期望大小和。

对于 j > 1 j>1 j>1,转移时要枚举第 i − 1 i-1 i1 个数为 k < j k<j k<j,考虑在最终序列中第 i i i 个数为 j j j 的前提下,第 i − 1 i-1 i1 个数为 k k k 的概率,不难发现它等于 a [ i − 1 ] [ k ] ∗ ( 1 − a [ i − 1 ] [ k ] ) ∑ s = 1 j − 1 a [ i − 1 ] [ s ] ∗ ( 1 − a [ i − 1 ] [ s ] ) \frac {a[i-1][k]*(1-a[i-1][k])}{\sum_{s=1}^{j-1}a[i-1][s]*(1-a[i-1][s])} s=1j1a[i1][s](1a[i1][s])a[i1][k](1a[i1][k])
因为是在最终序列中,所以必须要保证“稳定”,因此记 A [ i ] [ j ] = a [ i ] [ j ] ∗ ( 1 − a [ i − 1 ] [ j ] ) A[i][j]=a[i][j]*(1-a[i-1][j]) A[i][j]=a[i][j](1a[i1][j]) 表示第 i i i 个数为 j j j 且第 i − 1 i-1 i1 个数 < j <j <j 的概率。(特别的, A [ 2 ] [ 1 ] A[2][1] A[2][1] 的第二个数是可以为 2 的,因此不等于0)

所以对于 j > 1 j>1 j>1,有:
d p [ i ] [ j ] = j + ∑ k = 1 j − 1 d p [ i − 1 ] [ k ] ∗ A [ i − 1 ] [ k ] ∑ s = 1 j − 1 A [ i − 1 ] [ s ] dp[i][j]=j+\sum_{k=1}^{j-1}dp[i-1][k]*\frac {A[i-1][k]}{\sum_{s=1}^{j-1}A[i-1][s]} dp[i][j]=j+k=1j1dp[i1][k]s=1j1A[i1][s]A[i1][k]

而当 j = 1 j=1 j=1 时,要枚举第 i − 1 i-1 i1 个数为 k > 1 k>1 k>1,第一个生成的数必须是2,同理为 k k k 的概率为 b [ i − 1 ] [ k ] ∗ ( 1 − a [ i − 1 ] [ k ] ) ∑ s = 2 50 b [ i − 1 ] [ s ] ∗ ( a [ i − 1 ] [ s ] ) \frac {b[i-1][k]*(1-a[i-1][k])}{\sum_{s=2}^{50}b[i-1][s]*(a[i-1][s])} s=250b[i1][s](a[i1][s])b[i1][k](1a[i1][k]),记 B [ i ] [ j ] = b [ i ] [ j ] ∗ ( 1 − a [ i − 1 ] [ j ] ) B[i][j]=b[i][j]*(1-a[i-1][j]) B[i][j]=b[i][j](1a[i1][j]),则有:
d p [ i ] [ 1 ] = 1 + ∑ k = 2 50 d p [ i − 1 ] [ k ] ∗ B [ i − 1 ] [ k ] ∑ s = 2 50 B [ i − 1 ] [ s ] dp[i][1]=1+\sum_{k=2}^{50}dp[i-1][k]*\frac {B[i-1][k]}{\sum_{s=2}^{50}B[i-1][s]} dp[i][1]=1+k=250dp[i1][k]s=250B[i1][s]B[i1][k]

i > j i>j i>j A , B A,B A,B 不会随 i i i 改变,所以先暴力算出 d p [ 51 ] [ . . . ] dp[51][...] dp[51][...] (实际上dp[50]也没问题)后用 51*51 的矩阵加速DP就可以了。

最后 A n s = ∑ j = 1 50 A [ n ] [ j ] ∗ d p [ n ] [ j ] Ans=\sum_{j=1}^{50}A[n][j]*dp[n][j] Ans=j=150A[n][j]dp[n][j]

Code:

#include<bits/stdc++.h>
#define maxn 55
using namespace std;
const int N = 50;
int n;
double p,a[maxn][maxn],b[maxn][maxn],A[maxn][maxn],B[maxn][maxn],f[maxn][maxn],ans;
struct Mat{
	double s[maxn][maxn];
	Mat(){memset(s,0,sizeof s);}
	double* operator [] (int i){return s[i];}//awesome!
	Mat operator * (const Mat &B)const{
		Mat ret;
		for(int k=0;k<=N;k++) for(int i=0;i<=N;i++) if(s[i][k]) for(int j=0;j<=N;j++)
			ret.s[i][j]+=s[i][k]*B.s[k][j];
		return ret;
	}
}F,G;
int main()
{
	scanf("%d%lf",&n,&p),p/=1e9;
	a[1][1]=p,a[1][2]=b[1][2]=1-p;
	for(int i=2;i<=N;i++){
		a[i][1]=p,a[i][2]=1-p+p*p,b[i][2]=1-p;
		for(int j=3;j<=i+1;j++) 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=1;i<=N;i++) for(int j=1;j<=i+1;j++) A[i][j]=a[i][j]*(1-a[i-1][j]),B[i][j]=b[i][j]*(1-a[i-1][j]);
	for(int i=1;i<=N;i++) f[1][i]=i;
	for(int i=2;i<=N;i++){
		double s=0;
		for(int j=2;j<=N;j++) f[i][1]+=f[i-1][j]*B[i-1][j],s+=B[i-1][j];
		f[i][1]=f[i][1]/s+1;
		double x=0,y=0;
		for(int j=2;j<=N;j++) x+=f[i-1][j-1]*A[i-1][j-1],y+=A[i-1][j-1],f[i][j]=j+x/y;
	}
	if(n<=N){
		for(int j=1;j<=N;j++) ans+=A[n][j]*f[n][j];
		return printf("%.8f\n",ans),0;
	}
	F[0][0]=G[0][0]=1;
	for(int i=1;i<=N;i++) G[0][i]=i,F[0][i]=f[N][i];
	double s=0;
	for(int i=2;i<=N;i++) s+=B[N][i];
	for(int k=2;k<=N;k++) G[k][1]=B[N][k]/s;
	s=0;
	for(int j=2;j<=N;j++){
		s+=A[N][j-1];
		for(int k=1;k<j;k++) G[k][j]=A[N][k]/s;
	}
	n-=N;
	for(;n;n>>=1,G=G*G) if(n&1) F=F*G;
	for(int j=1;j<=N;j++) ans+=A[N][j]*F[0][j];
	printf("%.8f\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值