=== ===
这里放传送门
=== ===
题解
这题神死了。。。ATP想把那个在CF上强行加上【DP】这个tag的人吃掉。。。一开始吭哧了半天想了一个 O(n2h) 的东西根本不能做啊。。实际上这题就是一个推导,然后一个式子就出来了。。
还有,Bob这个人P事真TM多。。。。【(╯‵□′)╯︵┻━┻】
把CF的官方题解和翻译先链过来。果然还是中文看起来舒爽。。。那ATP这里就把题解上没有说的证明一点一点证一下吧。。。
首先这个题看起来是强行二合一实际上并没有。。因为可以证明如果给的是Bob的计数器,那么直接把N输出来就可以了。。因为如果Bob的计数器累加了 2i ,那么他所经过的楼的期望数目也是 2i 。
这是为什么呢。。首先我们可以发现Bob每次经过一条溜索的时候,它实际上经过的楼的数量应该包括【溜索下方那些高度较小的楼】还有【作为他终点的那栋楼】。也就是说,如果他实际上经过的楼数目是
i
,那么它的中间有
设当前它的计数器累加了 2H ,也就是说,它当前楼层的编号是H;也就是说,它当前楼层的高度是 H+1 (这道题恶心的地方之一就是楼层的编号和楼的高度还TM不一样= =)。那么下面那些高度较小的楼的高度必须都小于等于 H 。
那么我们分情况讨论,它经过的楼层数目的期望应该是
然后我们就可以算Bob经过楼层数目的期望了,它是:
把
(1−P)
全都提出来以后就是等差乘等比的形式啊,话说高中数学必修五第一章学了些啥啊。。错位相减求和啊。。。
用底下的减去上面的,得到:
两边能够约掉一个
1−P
辣!接着看中间那一坨等比数列,首先等比数列的求和公式是
a1(1−qn)1−q
,这里的
q
是满足
话说 P 是啥来着?把
Bob只是小杂鱼。。真正的Boss其实是Alice qwq。。。
搞定Alice的基本思路是先假设所有的楼都只有1层,也就是它们的编号都是0;那这样的话Bob的计数器肯定只能是 N ;然后再考虑加上一些高度更高的楼层,那么这种变化势必会加上一些溜索,那么Bob的计数器就要先【加上新溜索带来的贡献】再【减去被覆盖掉的旧溜索的贡献】。
我们一层一层把楼加高,对于新加入的编号能够达到
这两个显然是补集关系,算了一个就能算另一个。那么我们就算中间每一座塔的高度小于
H+1
的概率好了。设这个概率为
P
(众:怎么又是P你不觉得你字母重复了么。。ATP:你管我呢= =),那么有:
(和上面是一样的嘿嘿嘿)
那么两边的塔的高度都不小于 H+1 的概率就是 1−P=12H 。然后就可以知道,两座编号能够达到H的塔中间存在一条溜索的可能性就是:
那么如果已经知道存在这样一条连接两个编号为
H
的楼层的溜索,它们中间有多少被覆盖的旧溜索呢?显然就是最大编号为
注意这里的计算有一个条件就是已经确定了中间这
于是我们可以得到中间被覆盖的旧溜索数目的期望:
而由于塔的高度的随机性,这里的
L
显然可以是任意的。那么对于每个长度
至此终于把Alice搞定辣!这个式子用
O(nh)
的复杂度就可以很愉悦地搞出来!据说内层可以用矩阵乘法优化然后达到
O(hlogn)
的复杂度?好在出题人的良心还剩下了那么一点点
代码
话说那一坨式子写出来好恶心啊
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char w[10];
int N,H;
double ans,bin[60010];
double powww(double a,int t){
double ans=1;
while (t!=0){
if (t&1) ans=ans*a;
a=a*a;t>>=1;
}
return ans;
}
int main()
{
scanf("%s",w);
scanf("%d%d",&N,&H);
if (w[0]=='B'){
printf("%.10lf\n",(double)N);
return 0;
}
ans=N;bin[0]=1;
for (int i=1;i<=2*H;i++)
bin[i]=bin[i-1]*0.5;
for (int i=1;i<=H;i++)
for (int j=1;j<=N;j++){
double tmp=N-j;
tmp=tmp*bin[2*i]*powww(1-bin[i],j-1)*(1/bin[i]-1/bin[i-1]*(1+(double)(j-1)/(1/bin[i]-1)));
ans+=tmp;
}
printf("%.10lf\n",ans);
return 0;
}
偏偏在最后出现的补充说明
化式子这东西好神奇啊
把它化得它妈妈都不认识它了然后竟然能算出正确的结果来哈哈哈哈