传送门
题面翻译见:http://cogs.pro/cogs/problem/problem.php?pid=1921
神题啊…神哭了…
就知道Alice和Bob凑在一起肯定不干好事
想了一节晚自习+两节课,只yy出了一种不靠谱的 O(n2h) 的东西…
看题解发现不是dp,竟然是一道纯数学题…
要特别注意的是这道题的高度和编号是岔劈着的,非常恶心
cf官方题解:http://codeforces.com/blog/entry/8538
首先第二问的答案为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(1−12i)1−12=1−12i
令 S=s(i)
那么从某一栋楼的第i层出发,到达的大楼数的期望E计算为
E=(1∗S0+2∗S1+...+n∗Sn−1)∗(1−S) ….①(其中n为 +∞ )
这玩意竟然是数学中的差比数列,用什么错位相减法求E
S∗E=(1∗S1+2∗S2+...+n∗Sn)∗(1−S) …②
因为 Sn 趋近于0,并且①-②得
E=(1+S1+S2+...+Sn)=1+S∗(1−Sn)1−S=1+S1−S=11−S
将 S=s(i)=1−12i 代入得 E=2i
然后就证明了这个结论的正确性
上面的那个结论的逆命题是不成立的,当楼数为n时,Bob的得分会远远大于n
如何计算?
让我们每次将解的层数增加1。当H=0时,期望的估算值自然是n。现在对于加上的每一层,我们都可以加上这一层溜索的期望得分,并同时减去它们下方溜索的期望得分。最终我们将得到总共的期望得分。
对于编号为H的某一层(高度为H+1),让我们考虑左侧和右侧的一些塔,并计算它们之间存在一条溜索的可能性。令L是两座塔之间的距离。一条可能存在的,长度为L,高度为H+1的溜索的确存在,就必须满足它两端的塔都大于H(每个都有 1−s(H)=12H 的概率),并且中间的L-1座塔都小于等于H(每个都有 s(H)=1−12H 的概率)。因此这样一条溜索存在的概率是 (12H)2∗(1−12H)L−1 。
现在,假设这样一条溜索存在,它下方溜索的期望数量是多少?应当为高度为H(所在层编号为H-1)的塔的数量加1。
那么需要计算的就是,某一个塔高度为H(所在层编号为H-1)的概率是多少,特别注意前提是这个塔的高度至多是H(所在层编号为H-1)
设某一个塔的高度为H的概率为 P(H)=12H ,那么计算的答案应该为
对于每个长度L,在每一层都有n-L条长度为L的可能溜索。对于所有可能的溜索,我们把它出现的概率乘以它的花费,求和得到总的期望值。
对最终就是这个答案
据说数据范围比较大的话还可以用矩阵乘法优化
#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);
}