codeforces 335 E Counting Skyscrapers(概率期望)

题目链接

题目翻译

分析:
啊咧,为什么标签都是dp唉?
但是前辈都吐槽这道题根本不是dp啊。。。

前辈说有一个 O(n2h) O ( n 2 h ) 的dp(只针对已知Alice求Bob),xue微想了一下:
f[i][k] f [ i ] [ k ] 表示到第i栋楼,ta的高度为k时Bob计数器的期望
枚举与i连接的建筑物j,显然j~i之间不会有楼高于k,概率为: (k1)xhx,x=ij1 ( k − 1 ) x h x , x = i − j − 1 (因为Bob根本不可能到达h以上的高度,我们不用考虑)

显然这个dp一点用都没有(博主有毛病啊


正解

我们还是由浅到深

Bob->Alice

首先我们观察一下样例
啊咧,为什么输出的是一个整数不是real类型啊,好像有猫腻(不像是算出来的,倒像是读入后世界输出的
实际上,找到AC代码试一下,发现确实是输入什么输出什么啊
不要向某人一样投机取巧,我们是可以证明:

Bob的计数器累加了 2i 2 i ,那么ta所经过的楼的期望数目也是 2i 2 i

不严谨证明:

显然,Bob每使用一次ZIP运输线,实际上经过的楼数是:在运输线下高度较小的楼+终点楼
也就是说,如果ta经过了 i i 栋楼,那么就有 i1 i − 1 栋高度较小的楼在之间

设Bob当前计数器增加了 2H 2 H ,那么ta就在第 H H 层楼上
然而由于这个zz的编号方式, H H 层楼的高度实际上是 H+1 H + 1
那么ta经过的楼的高度就不会超过 H H

经过楼的期望=
1P(H)P(H) 1 ∗ P ( 中 间 没 有 高 度 ≤ H 的 楼 ) ∗ P ( 终 点 楼 的 高 度 ≥ H )
+2P(,H)P(H) + 2 ∗ P ( 中 间 有 一 栋 楼 , 高 度 ≤ H ) ∗ P ( 终 点 楼 的 高 度 ≥ H )
+P(,H)P(H)+... + ∗ P ( 中 间 有 两 栋 楼 , 高 度 ≤ H ) ∗ P ( 终 点 楼 的 高 度 ≥ H ) + . . .
P P 表示概率)

题目中给出,高度为 i i 的楼出现的概率是 2i=(12)i 2 − i = ( 1 2 ) i

因此,一栋楼的高度 H ≤ H 的概率:

P=(12)1+(12)2+(12)3+...+(12)H P = ( 1 2 ) 1 + ( 1 2 ) 2 + ( 1 2 ) 3 + . . . + ( 1 2 ) H

P=(12)H+112121 P = ( 1 2 ) H + 1 − 1 2 1 2 − 1

P=2H12H P = 2 H − 1 2 H

期望:

E=1P0(1P)+2P1(1P)+3P2(1P)+... E = 1 ∗ P 0 ∗ ( 1 − P ) + 2 ∗ P 1 ∗ ( 1 − P ) + 3 ∗ P 2 ∗ ( 1 − P ) + . . .

E=(1P)(1P0+2P1+3P2+...) E = ( 1 − P ) ∗ ( 1 ∗ P 0 + 2 ∗ P 1 + 3 ∗ P 2 + . . . )

混合等差等比数列,错位相消:
E=(1P)(1P0+2P1+3P2+...) E = ( 1 − P ) ∗ ( 1 ∗ P 0 + 2 ∗ P 1 + 3 ∗ P 2 + . . . ) ①

PE=(1P)(1P1+2P2+3P3+...) P ∗ E = ( 1 − P ) ∗ ( 1 ∗ P 1 + 2 ∗ P 2 + 3 ∗ P 3 + . . . ) ②

①-②,得:
(1P)E=(1P)(P0+P1+P2+...) ( 1 − P ) ∗ E = ( 1 − P ) ∗ ( P 0 + P 1 + P 2 + . . . )

E=11P E = 1 1 − P

其中, P=2H12H P = 2 H − 1 2 H
E=2H E = 2 H

Alice->Bob

首先我们假设所有的楼都只有一层,编号都是0
这种情况下,Bod的计数器得到的一定是 n n

之后我们要加上一些高度更高的楼层,这样楼层之间就会出现一些ZIP运输线
此时,Bob计数器的答案就应该+运输线的贡献-覆盖掉的楼的贡献

我们一层一层的把楼加高,对于新加入的编号能达到 H H 的楼
这两栋楼的编号分别为 i,j i , j ,设 L=ji L = j − i ,如果这两栋楼的第 H H 层之间有运输线
那么一定满足: i i j j 的高度 H+1 ≥ H + 1 ij i − j 之间的楼的高度 H 小 于 等 于 H
之前我们已经求出一栋楼的高度 H ≤ H 的概率: P=2H12H P = 2 H − 1 2 H
显然,一栋楼的高度 H+1 ≥ H + 1 的概率: 1P=12H 1 − P = 1 2 H

因此,两栋楼层编号能够达到 H H 的楼中间存在一条运输线的概率:

(1P)2PL1=(12H)2(2H12H)L1=(12H)2(112H)L1 ( 1 − P ) 2 ∗ P L − 1 = ( 1 2 H ) 2 ∗ ( 2 H − 1 2 H ) L − 1 = ( 1 2 H ) 2 ∗ ( 1 − 1 2 H ) L − 1

因为我们是逐层计算的,所以只剩下 H1 H − 1 层的运输线没有消除影响

那么如果已经知道存在这样一条运输线,中间有多少被覆盖的旧溜索呢?

显然就是最大层数为 H1 H − 1 ,高度为 H H 的塔的数量+1

那么我们只需要高度恰好为 H H 的楼的期望数目就可以了

注意这里的计算有一个条件:中间的 L1 L − 1 栋楼的高度一定不超过 H H
下面考虑用概率公式计算
有公式(在 B B 条件成立的前提下, A A 发生的概率)

P(A|B)=P(AB)P(B) P ( A | B ) = P ( A ∩ B ) P ( B )

其中 A=H A = 楼 的 高 度 恰 好 为 H B=H B = 楼 的 高 度 不 超 过 H
显然
P(AB)=P(A)=12H P ( A ∩ B ) = P ( A ) = 1 2 H

P(B)=2H12H P ( B ) = 2 H − 1 2 H

那么一栋楼高度恰好为 H H 的概率为 12H2H2H1=12H1 1 2 H ∗ 2 H 2 H − 1 = 1 2 H − 1

=1P(L1H) 期 望 = 1 ∗ P ( L − 1 栋 楼 的 高 度 都 小 于 H )
+2P(H)P(L2H) + 2 ∗ P ( 一 栋 楼 的 高 度 为 H ) ∗ P ( L − 2 栋 楼 的 高 度 都 小 于 H )
+3P(H)P(L3H) + 3 ∗ P ( 两 栋 楼 的 高 度 为 H ) ∗ P ( L − 3 栋 楼 的 高 度 都 小 于 H )
+...+LP(L1H) + . . . + L ∗ P ( L − 1 栋 楼 的 高 度 为 H )

E=1(112H1)L1+212H1(112H1)L2+...+L(12H1)L1 E = 1 ∗ ( 1 − 1 2 H − 1 ) L − 1 + 2 ∗ 1 2 H − 1 ∗ ( 1 − 1 2 H − 1 ) L − 2 + . . . + L ∗ ( 1 2 H − 1 ) L − 1

E=1+L12H1 E = 1 + L − 1 2 H − 1

对于每个长度 L L ,在当前层都存在 NL N − L 个可能位置出现溜索
那么先枚举高度,再从枚举 L L ,综合上面的推导,我们就能得到如下的计算式:

n+i=1hj=1n(nj)(12i)2(112i)j1(2i2i1(1+j12i1)) n + ∑ i = 1 h ∑ j = 1 n ( n − j ) ∗ ( 1 2 i ) 2 ∗ ( 1 − 1 2 i ) j − 1 ∗ ( 2 i − 2 i − 1 ∗ ( 1 + j − 1 2 i − 1 ) )

两栋楼层编号能够达到 H H 的楼中间存在一条运输线的概率: (12i)2(112i)j1 ( 1 2 i ) 2 ∗ ( 1 − 1 2 i ) j − 1
层数为 i i 的运输线贡献: 2i 2 i
需要减掉的被覆盖的运输线贡献: 2i1 2 i − 1
层数为 i1 i − 1 (被覆盖)的运输线的期望: 1+j12i1 1 + j − 1 2 i − 1

这个式子用 O(nh) O ( n h ) 的复杂度就可以完成
dada们表示内层可以用矩阵乘法优化然后达到 O(hlogn) O ( h l o g n ) 的复杂度

tip

这道题真的光速弃疗

官方题解

#include
#include
#include

using namespace std;

char s[10];
int n,h;
double mi[200];

double KSM(double a,int b) {
    double t=1.0;
    while (b) {
        if (b&1) t=t*a;
        a=a*a;
        b>>=1;
    }
    return t;
}

int main() {
    scanf("%s",s);
    scanf("%d%d",&n,&h);

    if (s[0]=='B') {
        printf("%d\n",n);
        return 0;
    }

    double ans=(double)n;
    mi[0]=1.0;
    for (int i=1;i<=2*h;i++) mi[i]=mi[i-1]*2.0;
    for (int i=1;i<=h;i++)
        for (int j=1;j<=n;j++) {
            double t=KSM(1.0-1.0/mi[i],j-1);
            ans+=1.0*(n-j)*(1.0/mi[2*i])*t*(mi[i]-mi[i-1]*(1+(double)(j-1)/(mi[i]-1.0)));
        }
    printf("%.10lf\n",ans);

    return 0;
}

你可能感兴趣的:(省选,概率期望)