codeforces 335 E Counting Skyscrapers(概率期望)

本文探讨了一种特殊的计数器累加问题,通过数学推导详细解释了如何计算两个角色间通过特定路径移动时,计数器的期望值变化。文章首先分析了递归动态规划方法的不足之处,接着提出了一种新颖且高效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

题目翻译

分析:
啊咧,为什么标签都是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栋高度较小的楼在之间

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

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

题目中给出,高度为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的楼
这两栋楼的编号分别为 i,j i , j ,设 L=ji L = j − i ,如果这两栋楼的第 H H 层之间有运输线
那么一定满足:i j j 的高度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

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

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

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

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

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

P(A|B)=P(AB)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

=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个可能位置出现溜索
那么先枚举高度,再从枚举 L L ,综合上面的推导,我们就能得到如下的计算式:

n+i=1hj=1n(nj)(12i)2(112i)j1(2i2i1(1+j12i1))

两栋楼层编号能够达到 H H 的楼中间存在一条运输线的概率:(12i)2(112i)j1
层数为 i i 的运输线贡献:2i
需要减掉的被覆盖的运输线贡献: 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<cstring>
#include<cstdio>
#include<iostream>

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;
}
### Codeforces 887E Problem Solution and Discussion The problem **887E - The Great Game** on Codeforces involves a strategic game between two players who take turns to perform operations under specific rules. To tackle this challenge effectively, understanding both dynamic programming (DP) techniques and bitwise manipulation is crucial. #### Dynamic Programming Approach One effective method to approach this problem utilizes DP with memoization. By defining `dp[i][j]` as the optimal result when starting from state `(i,j)` where `i` represents current position and `j` indicates some status flag related to previous moves: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = ...; // Define based on constraints int dp[MAXN][2]; // Function to calculate minimum steps using top-down DP int minSteps(int pos, bool prevMoveType) { if (pos >= N) return 0; if (dp[pos][prevMoveType] != -1) return dp[pos][prevMoveType]; int res = INT_MAX; // Try all possible next positions and update 'res' for (...) { /* Logic here */ } dp[pos][prevMoveType] = res; return res; } ``` This code snippet outlines how one might structure a solution involving recursive calls combined with caching results through an array named `dp`. #### Bitwise Operations Insight Another critical aspect lies within efficiently handling large integers via bitwise operators instead of arithmetic ones whenever applicable. This optimization can significantly reduce computation time especially given tight limits often found in competitive coding challenges like those hosted by platforms such as Codeforces[^1]. For detailed discussions about similar problems or more insights into solving strategies specifically tailored towards contest preparation, visiting forums dedicated to algorithmic contests would be beneficial. Websites associated directly with Codeforces offer rich resources including editorials written after each round which provide comprehensive explanations alongside alternative approaches taken by successful contestants during live events. --related questions-- 1. What are common pitfalls encountered while implementing dynamic programming solutions? 2. How does bit manipulation improve performance in algorithms dealing with integer values? 3. Can you recommend any online communities focused on discussing competitive programming tactics? 4. Are there particular patterns that frequently appear across different levels of difficulty within Codeforces contests?
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值