[CSP-S模拟测试]:简单的期望(DP)

题目描述

从前有个变量$x$,它的初始值已给出。
你会依次执行$n$次操作,每次操作有$p\%$的概率令$x=x\times 2$,$(100−p)\%$的概率令$x=x+1$。
假设最后得到的值为$w$,令$d$为$w$的质因数分解中$2$的次数,求$d$的期望。


输入格式

从文件$exp.in$中读入数据。
第一行三个整数$x,n,p$,含义见题目描述。


输出格式

输出到文件$exp.out$中。
一行一个实数,表示$d$的期望。
如果你的答案与标准答案的误差不超过$10^{−6}$,则判定为正确。


样例

样例输入1:

1 1 50

样例输出1:

1.0000000000

样例输入2:

5 3 0

样例输出2:

3.0000000000

样例输入3:

5 3 25

样例输出3:

1.9218750000


数据范围与提示

对于$20\%$的数据,$n\leqslant 20$;
对于$30\%$的数据,$n\leqslant 50$;
对于$50\%$的数据,$n\leqslant 100$;
对于$100\%$的数据,$x\leqslant 10^9,n\leqslant 200,0\leqslant p\leqslant 100$。


题解

首先,质因数分解中$2$的个数即为二进制表示下末尾$0$的个数。

考虑$DP$,设$dp[i][s][j][0/1]$表示进行第$i$次操作后,当前数的二进制表示后$8$位为$s$,第九位为$0/1$,从第九位开始往前连续$j$位相同的概率。

先来解释后$8$位的原因,因为至少要$2^8+1$次操作才可以向$9$进位,而操作数只有$200$,所以不会对答案造成贡献。

再来解释$j$的用途,假如后$8$位都是$1$第九位也是$1$,那么我们在进行$+1$的操作后会变成$1000……$,而$j$是为了计算出这个$1$的位置。

转移一共有$8$个,不再一一赘述,具体可以看代码。

时间复杂度:$\Theta(2^8n^2)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int X,N,P,mx;
double gl1,gl2;
double dp[201][257][300][2];
double ans;
void pre_work()
{
	int flag=X>>8,sum=0;
	for(int i=flag&1;(flag&1)==i&&flag;sum++)flag>>=1;
	if(!sum)sum++;mx=N+sum;
	dp[0][X&255][sum][(X>>8)&1]=1;
}
int judge(int x)
{
	int res=0;
	while(!(x&1)){res++;x>>=1;}
	return res;
}
int main()
{
	scanf("%d%d%d",&X,&N,&P);
	gl1=(double)P/100;
	gl2=(double)(100-P)/100;
	pre_work();
	for(int i=0;i<N;i++)
		for(int s=0;s<256;s++)
			for(int j=1;j<=mx;j++)
			{
				if(!(((s<<1)&256)>>8))dp[i+1][(s<<1)&255][j+1][0]=dp[i+1][(s<<1)&255][j+1][0]+dp[i][s][j][0]*gl1;
				else dp[i+1][(s<<1)&255][1][1]=dp[i+1][(s<<1)&255][1][1]+dp[i][s][j][0]*gl1;
				if(((s<<1)&256)>>8)dp[i+1][(s<<1)&255][j+1][1]=dp[i+1][(s<<1)&255][j+1][1]+dp[i][s][j][1]*gl1;
				else dp[i+1][(s<<1)&255][1][0]=dp[i+1][(s<<1)&255][1][0]+dp[i][s][j][1]*gl1;
				if(s==255)dp[i+1][0][1][1]=dp[i+1][0][1][1]+dp[i][255][j][0]*gl2;
				else dp[i+1][s+1][j][0]=dp[i+1][s+1][j][0]+dp[i][s][j][0]*gl2;
				if(s==255)dp[i+1][0][j][0]=dp[i+1][0][j][0]+dp[i][255][j][1]*gl2;
				else dp[i+1][s+1][j][1]=dp[i+1][s+1][j][1]+dp[i][s][j][1]*gl2;
			}
	for(int s=1;s<256;s++)
		for(int j=1;j<=mx;j++)
			ans+=(dp[N][s][j][0]+dp[N][s][j][1])*judge(s);
	for(int j=1;j<=mx;j++)ans+=dp[N][0][j][0]*(j+8)+dp[N][0][j][1]*8;
	printf("%.6lf",ans);
	return 0;
}

rp++

posted @ 2019-10-14 11:50  HEOI-动动  阅读(380)  评论(0编辑  收藏  举报