CodeForces 235B Let's Play Osu!

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

题意:有一组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

 

posted @ 2013-11-05 10:52  浙西贫农  阅读(429)  评论(1编辑  收藏  举报