CodeForces 235B Let's Play Osu!

本文解析了一道关于OX序列的算法题目,讨论了如何计算给定序列中每个位置为O的概率时,连续O序列得分期望值的问题。通过巧妙利用组合数学公式与概率论原理,实现了O(n)复杂度的高效算法。

敲完此题我在想,或许我的智商不适合再搞下去了。。

题意:有一组OX序列(长度<=10^5),得分为每组连续的O序列的个数的平方和,现给出序列中每个位置上字母为O的概率,求得分的期望。

一开始想递推,怎么想都是n^2的,看看这个数据范围,肯定不能是n^2,也没有办法拿出什么nlogn的,就在想,难道真的是O(n)的?

结果一搜题解,擦。。。。这么短的代码,果然是O(n)的。

搜到了几篇题解,神犇们留下一句,利用这个公式:2C(n,2)+n=n^2就可以递推了。天哪,果然都是大神,我看半天就除了看出来这个公式是对的就再也看不出来这究竟是什么意思了。。

然后看了官方题解:http://codeforces.com/blog/WJMZBMR 才慢慢搞懂。。

大概的意思是这样的:对于长度为n的一个连续的只含有‘O’的串,其中包含C(n,2)对的O,这C(n,2)对的O每对可以贡献2分,另外,每个O的存在都可以贡献1分,因此,对于长度为n的一个连续的只含有‘O’的串,它的得分n^2可以拆解成2C(n,2)+n。

也就是说,只要出现了一个连续的只含有‘O’的串s(i)……s(j),它对总分的贡献都是2分,对期望的贡献是2*p(i)*p(i+1)*p(i+2)*……*p(j-1)*p(j-2)。

为什么呢。。这里我又想了半天,似乎明白了为什么别人说做期望的时候尽量把权值和概率分离。(这里分离了以后每个s(i)……s(j)为O的串的贡献都是2分,做到了权值不会变化)

举个例子,比如说有个长度为3的串,在每个位置上出现O的概率分别为 0.6 0.5 0.4

来看一下OOO的得分组成:

1.OO_ 2._OO 3.O_O 连续的O共存在3对O。3*2=6分

2.有3个O存在,每个可以贡献1分。3*1=3分

再算一下OOO对应的期望:0.6*0.5*0.4*9=1.08

1.08怎么来的呢?

首先OOO的存在贡献2分,概率为0.6*0.5*0.4,对总期望的贡献是0.6*0.5*0.4*2=0.24

接下来,OO_的存在贡献2分,概率为0.6*0.5,对总期望的是0.6*0.5*2=0.6。但是,这0.6是对总期望的贡献,第三个是_的位置我们并不知道它是什么,_有0.4的概率是O,因此,它对OOO的期望的贡献是0.6*0.4=0.24,剩下的0.36贡献给了OOX。

同理,_OO的存在贡献2分,概率为0.5*0.4,对总期望的是0.5*0.4*2=0.4。但是,这0.4是对总期望的贡献,第一个是_的位置我们并不知道它是什么,_有0.6的概率是O,因此,它对OOO的期望的贡献是0.4*0.6=0.24,剩下的0.24贡献给了XOO。

那么连续的O对OOO的期望的贡献是0.24*3=0.72。

另外,每个O都会做出贡献,第一个O对总期望贡献了1*0.6=0.6,其中,只有0.6*0.5*0.4=0.12的部分(对应后面2个都是O)贡献给了OOO。同理,另外两个O对总期望的贡献是1*0.5和1*0.4,但是他们对OOO贡献的那部分都是0.12,因此3个O单独来算对OOO的总贡献是0.36,OOO的总期望是0.72+0.36=1.08。

这说明了什么?说明了不管_位置上放O还是X,它对总期望的贡献都是可以确定的,只不过这个对总期望的贡献还会分配到不同的方案里面去。

然后,就产生了这么短的代码:

 1 #include <iostream>
 2 #include <iomanip>
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     double ans = 0.0,pre = 0.0,now;
 8     int n;
 9     cin>>n;
10     while(n--){
11         cin>>now;
12         pre *= now;
13         ans += 2*pre+now;
14         pre += now;
15     }
16     cout<<fixed<<setprecision(15)<<ans<<endl;
17     return 0;
18 }
View Code

 

转载于:https://www.cnblogs.com/zhexipinnong/p/3408095.html

### 关于 Codeforces 1853B 的题解实现 尽管当前未提供关于 Codeforces 1853B 的具体引用内容,但可以根据常见的竞赛编程问题模式以及相关算法知识来推测可能的解决方案。 #### 题目概述 通常情况下,Codeforces B 类题目涉及基础数据结构或简单算法的应用。假设该题目要求处理某种数组操作或者字符串匹配,则可以采用如下方法解决: #### 解决方案分析 如果题目涉及到数组查询或修改操作,一种常见的方式是利用前缀和技巧优化时间复杂度[^3]。例如,对于区间求和问题,可以通过预计算前缀和数组快速得到任意区间的总和。 以下是基于上述假设的一个 Python 实现示例: ```python def solve_1853B(): import sys input = sys.stdin.read data = input().split() n, q = map(int, data[0].split()) # 数组长度和询问次数 array = list(map(int, data[1].split())) # 初始数组 prefix_sum = [0] * (n + 1) for i in range(1, n + 1): prefix_sum[i] = prefix_sum[i - 1] + array[i - 1] results = [] for _ in range(q): l, r = map(int, data[2:].pop(0).split()) current_sum = prefix_sum[r] - prefix_sum[l - 1] results.append(current_sum % (10**9 + 7)) return results print(*solve_1853B(), sep='\n') ``` 此代码片段展示了如何通过构建 `prefix_sum` 来高效响应多次区间求和请求,并对结果取模 \(10^9+7\) 输出[^4]。 #### 进一步扩展思考 当面对更复杂的约束条件时,动态规划或其他高级技术可能会被引入到解答之中。然而,在没有确切了解本题细节之前,以上仅作为通用策略分享给用户参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值