CF441E

首先%xtw,从她那边得到了一个非常巧妙的思路。
首先不难发现,因为最多进行 \(200\) 次操作,所以转成二进制之后前 \(8\) 位的数字是不会影响后面的位数的,因为每次 \(\times2\) 相当于是整体左移一位,相对距离没有影响,而 \(+1\) 能够变化的值不会超过 \(256\)

\(g_{i,s}\) 表示操作 \(i\) 次前八位为 \(s\) 的期望是多少,可以很轻松的维护出答案。

然后它就假了

如果这样写,会发现它 Wa 5 原因是前面四个点都很水

考虑一下,会发现是漏掉了 \(000000000.....00000\) 的情况,即这八位之外还有 \(0\) 的情况。

很好解决,只需要多开一个数组 \(f_{i,s}\) 表示操作 \(i\) 次得到 \(s\) 的期望后缀 \(0\) 长度就好了,前面转移完全一致,只是 \(s=0\) 的时候需要特判。

然后它还是假了

再思考一下,会发现还是漏了一种情况, \(111111111111.....11111111\),这种情况下再 \(+1\) 会出现一大串的后缀 \(0\) ,而前面那个dp并不能把这个统计进去。

没关系,再开一个数组!
\(h_{i,j,s}\) 表示操作 \(i\) 次去掉前八位之后后缀 \(1\) 的个数为 \(j\),前八位为 \(s\) 的期望是多少。比如 \((1100001000)_2\) 对应的 \(j\) 就是 \(2\) ,对应的 \(s\) 就是 \((00001000)_2\)

会发现这一部分的转移还是和 \(f,g\) 的差不多,实际上只需要稍微改一下判断就好了。

实际上这一个部分可以和上面的 \(f,g\) 一样开两个数组来实现 \(O(k^2)\) 的时空复杂度。但是我懒

有人或许会提出疑问:这样子不是还是会有一些情况没法判断吗?比如 \(111110\ 11111111\) ,这样再 \(+1\) 不就无法统计上前面新链接上的后缀 \(1\) 吗?

这个确实无法统计,但是可以发现,如果要连接上前面的这一串后缀 \(1\) ,意味着需要再进行 \(255\)\(+1\) 操作,因此这种情况并不会被统计,贡献为 \(0\),不需要考虑。

无论是理解上还是代码长度上均被另一种方法吊打

实现的精细一些是可以做到空间 \(O(k)\),时间 \(O(k^2)\) 的,本人写的是 \(O(k^3)\)

至于dp具体转移懒得敲公式了,就没在上面说,可以参考下面的代码。

Code

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ri register int
#define ll long long
#define ui unsigned int
il ll read(){
    bool f=true;ll x=0;
    register char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=false;ch=getchar();}
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    if(f) return x;
    return ~(--x);
}
il void write(const ll &x){if(x>9) write(x/10);putchar(x%10+'0');}
il void print(const ll &x) {x<0?putchar('-'),write(~(x-1)):write(x);putchar('\n');}
il ll max(const ll &a,const ll &b){return a>b?a:b;}
il ll min(const ll &a,const ll &b){return a<b?a:b;}
#define double long double
double f[2][256],g[2][256],h[2][235][256],p;
int count(int x){
    int ans=0;
    for(;x&&!(x&1);x>>=1) ++ans;
    return ans;
}
int ccount(int x){
    int ans=0;
    for(;x&1;x>>=1) ++ans;
    return ans;
}
int cnt[256],cntc[256],x,n;
#define B 255
int main(){
    cnt[0]=1,cntc[1]=1;
    for(ri i=2;i<256;i+=2) cnt[i]=cnt[i>>1]+1;
    for(ri i=3;i<256;i+=2) cntc[i]=cntc[i>>1]+1;
    x=read(),n=read(),p=1.0*read()/100;
    f[0][x&B]=count(x),g[0][x&B]=1;
    h[0][ccount(x>>8)][x&B]=1;
    for(ri i=1,o=1;i<=n;++i,o^=1){
        memset(h[o],0,sizeof(h[o]));
        for(ri j=0;j<232;++j){
            for(ri s=0;s<=255;++s){
                if(s&128)  h[o][j+1][(s<<1)&B]+=h[o^1][j][s]*p;
                else h[o][0][(s<<1)&B]+=h[o^1][j][s]*p;
                if(s==255){
                    if(j) h[o][0][0]+=h[o^1][j][s]*(1-p);
                    else h[o][1][0]+=h[o^1][j][s]*(1-p);
                }
                else h[o][j][s+1]+=h[o^1][j][s]*(1-p);
            }
        }
        double res=0;
        for(ri j=0;j<232;++j) res+=h[o^1][j][B]*(8+j);
        f[o][0]=(f[o^1][0]+g[o^1][0]+f[o^1][128]+g[o^1][128])*p+res*(1-p);
        g[o][0]=(g[o^1][0]+g[o^1][128])*p+g[o^1][B]*(1-p);
        for(ri j=1;j<=255;++j){
            g[o][j]=g[o^1][j-1]*(1-p);
            if(!(j&1)) g[o][j]+=(g[o^1][j>>1]+g[o^1][j>>1|128])*p;
            f[o][j]=g[o][j]*cnt[j];
        }
    }
    double ans=0;
    for(ri i=0;i<256;++i) ans+=f[n&1][i];
    printf("%.9Lf",ans);
    return 0;
}
/*
224314221 19 51
1.590075252
*/
posted @ 2021-07-20 21:05  krimson  阅读(47)  评论(0编辑  收藏  举报