[Codeforces335E]Counting Skyscrapers(概率期望)

题目描述

传送门
题面翻译见:http://cogs.pro/cogs/problem/problem.php?pid=1921

题解

神题啊…神哭了…
就知道Alice和Bob凑在一起肯定不干好事
想了一节晚自习+两节课,只yy出了一种不靠谱的 O(n2h) 的东西…
看题解发现不是dp,竟然是一道纯数学题…
要特别注意的是这道题的高度和编号是岔劈着的,非常恶心
cf官方题解:http://codeforces.com/blog/entry/8538

Part 1 Bob->Alice

首先第二问的答案为n
也就是说Bob手中的计数器是n那么大楼数的期望也是n
只需要证明:从某一栋楼的第i层出发,得分为 2i ,期望到达的大楼数也是 2i

如何计算从某一栋楼的第i层出发的期望到达的大楼数?
容易知道假设到达了x栋大楼,那么用x-1栋(中间的一坨)的高度不能超过i,最后一栋的高度不能低于i+1
需要注意的是,从第i层出发,实际上出发的高度是i+1,所以中间的楼的高度应该小于等于i

首先设某一栋楼的高度小于等于i的概率为s(i)
那么 s(i)=12+122+...+12i=12(112i)112=112i
S=s(i)
那么从某一栋楼的第i层出发,到达的大楼数的期望E计算为
E=(1S0+2S1+...+nSn1)(1S) ….①(其中n为 +
这玩意竟然是数学中的差比数列,用什么错位相减法求E
SE=(1S1+2S2+...+nSn)(1S) …②
因为 Sn 趋近于0,并且①-②得
E=(1+S1+S2+...+Sn)=1+S(1Sn)1S=1+S1S=11S
S=s(i)=112i 代入得 E=2i
然后就证明了这个结论的正确性

Part 2 Alice->Bob

上面的那个结论的逆命题是不成立的,当楼数为n时,Bob的得分会远远大于n
如何计算?

让我们每次将解的层数增加1。当H=0时,期望的估算值自然是n。现在对于加上的每一层,我们都可以加上这一层溜索的期望得分,并同时减去它们下方溜索的期望得分。最终我们将得到总共的期望得分。

对于编号为H的某一层(高度为H+1),让我们考虑左侧和右侧的一些塔,并计算它们之间存在一条溜索的可能性。令L是两座塔之间的距离。一条可能存在的,长度为L,高度为H+1的溜索的确存在,就必须满足它两端的塔都大于H(每个都有 1s(H)=12H 的概率),并且中间的L-1座塔都小于等于H(每个都有 s(H)=112H 的概率)。因此这样一条溜索存在的概率是 (12H)2(112H)L1

现在,假设这样一条溜索存在,它下方溜索的期望数量是多少?应当为高度为H(所在层编号为H-1)的塔的数量加1。
那么需要计算的就是,某一个塔高度为H(所在层编号为H-1)的概率是多少,特别注意前提是这个塔的高度至多是H(所在层编号为H-1)
设某一个塔的高度为H的概率为 P(H)=12H ,那么计算的答案应该为

P(H)P(1)+P(2)+...+P(H)=12H12+122+...+12H=12H112H=12H1

所以某一个塔高度为H(所在层的编号为H-1)的概率为 12H1
那么L-1座塔中的每一个都有1/(2^H-1)的概率高度为H(所在层编号为H-1),因此一个长度为L,高度为H的溜索下方溜索的期望数量是 1+L12H1

对于每个长度L,在每一层都有n-L条长度为L的可能溜索。对于所有可能的溜索,我们把它出现的概率乘以它的花费,求和得到总的期望值。

E=n+i=1hj=1n(nj)122i(112i)j1(2i2i1(1+j12i1))

对最终就是这个答案
据说数据范围比较大的话还可以用矩阵乘法优化

代码

#include
#include
#include
#include
#include
using namespace std;

int n,h;
char name[10];
double mi[100],ans;

double fast_pow(double a,int p)
{
    double ans=1;
    for (;p;p>>=1,a*=a)
        if (p&1)
            ans*=a;
    return ans;
}
int main()
{
    scanf("%s",name);
    scanf("%d%d",&n,&h);
    if (name[0]=='B')
    {
        printf("%d\n",n);
        return 0;
    }
    mi[0]=1.0;
    for (int i=1;i<=2*h;++i) mi[i]=mi[i-1]*2.0;
    ans=(double)n;
    for (int i=1;i<=h;++i)
        for (int j=1;j<=n;++j)
        {
            double poww=fast_pow(1-1/mi[i],j-1);
            ans+=((double)n-(double)j)*(1/mi[2*i])*poww*(mi[i]-mi[i-1]*(1+(j-1)/(mi[i]-1)));
        }
    printf("%.9lf\n",ans);
}

你可能感兴趣的:(题解,概率期望)