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