[Codeforces335E]Counting Skyscrapers(概率期望)

来源:互联网 发布:火车头采集js分页 编辑:程序博客网 时间:2024/05/16 10:01

题目描述

传送门
题面翻译见: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<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>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);}
0 0
原创粉丝点击