luo‘s oj P1917 简单的期望(exp)

传送门

分析

令 dp[ i ][ s ][ j ][ k ] 为进行了 i 次操作,当前数的二进制表示中后 8 位为 s ,第 9 位为 0 或 1,并且从第 9 位开始有连续 j 位相同的概率。转移挺麻烦的但是很显然,这里就不写了(写在代码里了QwQ)
直接贴题解
解释一下取后 8 位的原因。考虑对一个这样的数进行 +1 操作:……11011111111,我们将第 9 位的 0 进成 1 时,前面有多少个连续的 1 是未知的,如果之后再产生一次进位,这些未知个数的 1 都会变成 0,而它们都会对答案产生贡献,由于个数未知,无法将这些贡献计入答案。而取后 8 位时,最少需要 2^8 次 +1 操作才会产生第二次进位,而操作总数最多只有 200,因此进位次数最多 1,不会对答案产生影响。
时间复杂度O(2^8*n^2) 。

#include<bits/stdc++.h>
#define MaxM 232//(2^31-1)*2^200//所以连续的有232-8但是可能越界,于是开的稍微大一点 
#define N 205
#define M 255//(1<<8)-1 
using namespace std;
int x,n,p;double f[MaxM+1][M+1][MaxM+1][2];
int main(){
    scanf("%d%d%d",&x,&n,&p);
    double p1=p*0.01;
    double p2=1.00-p1;
    int up=x>>8;//取第九位起的数 
    int bit=up&1,cnt=0;//cnt记录初始的边界的第九位0的连续个数 
    while(up&&(up&1)==bit)up>>=1,++cnt;//基本运算 
    f[0][x&M][cnt][bit]=1.0;
    for(int i=0;i<n;i++)
        for(int s=0;s<=M;s++)
            for(int j=0;j<MaxM;j++)
                for (int k=0;k<2;k++){
                    double now=f[i][s][j][k];
                    //乘二的情况 
                    bit=s>>7&1;//取第八位作为新的第九位 
                    cnt= bit==k ? j+1:1;//第九位相同或不同得到新的最长连续概率 
                    f[i+1][s<<1&M][cnt][bit]+=now*p1;//&M以保持八位,转移 
                    //加一的情况 
                    bit= s==M ? !k:k;//如果是全一改变第九位 
                    cnt= s==M&&!k ? 1:j;//第九位进位且第九位为1的话,连续的长度变为1,否则不变 
                    f[i+1][s+1&M][cnt][bit]+=now*p2;//同理 
                }
    double ans=0.0;
    for(int s=0;s<=M;s++)
        for(int i=0;i<MaxM;i++)
            for(int j=0;j<2;j++)
                ans+=f[n][s][i][j]*(s ? __builtin_ctz(s):8+(j ? 0:i));//这里的__builtin_ctz():x是求一个数的末尾x位连续0的个数 
                //其实就是计算所谓的最后连续的0的位数 
    printf("%.4f\n", res);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36316033/article/details/81225931
OJ