题目背景
原 维护队列 参见 P1903
题目描述
某一天WJMZBMR在打osu~~~但是他太弱逼了,有些地方完全靠运气:(
我们来简化一下这个游戏的规则
有 nn 次点击要做,成功了就是 o
,失败了就是 x
,分数是按 combo 计算的,连续 aa 个 combo 就有 a\times aa×a 分,combo 就是极大的连续 o
。
比如ooxxxxooooxxx
,分数就是 2 \times 2 + 4 \times 4 = 4 +16=202×2+4×4=4+16=20。
Sevenkplus 闲的慌就看他打了一盘,有些地方跟运气无关要么是 o
要么是 x
,有些地方 o
或者 x
各有 50\%50% 的可能性,用 ?
号来表示。
比如 oo?xx
就是一个可能的输入。 那么 WJMZBMR 这场 osu 的期望得分是多少呢?
比如 oo?xx
的话,?
是 o
的话就是 oooxx
(99),是x的话就是 ooxxx
(44),期望自然就是 (4+9)/2 =6.5(4+9)/2=6.5 了。
输入格式
第一行一个整数 nn(n\le3\times10^5n≤3×105),表示点击的个数
接下来一个字符串,每个字符都是 o
,x
,?
中的一个
输出格式
一行一个浮点数表示答案
四舍五入到小数点后 44 位
如果害怕精度跪建议用 long double 或者 extended。
输入输出样例
输入 #1复制
4 ????
输出 #1复制
4.1250
这道题的思路
定义两个数组g[i]和f[i]
记录相关的情况
g[i]为记率连续情况下的概率
有前缀和的思想
f[i]记录答案的总和
1.当字符为‘x’的时候
g[i] = 0; f[i[ =f[i-1];
2.当字符为‘o’的时候
g[i] = g[i-1].f[i] = f[i-1]-g[i-1]的平方+g[i]的平方
化简得到 f[i] = f[i-1]+2g[i-1]+1;
3.当字符为‘?’的时候
两种情况相加除以2得
g[i] = 0.5g[i-1]+0.5 f[i] = f[i-1]+g[i-1]+0.5
代码如下
#include<bits/stdc++.h>
#include<stdio.h>
#define rep(a,b,c ) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a++)
const int N = 3e5+10;
double g[N];
double f[N];
using namespace std;
int main(){
int n;
cin>>n;
char c;
rep(i,1,n)
{
cin>>c;
if(c =='x')
{
g[i] = 0,f[i] = f[i-1];
}
else if(c =='o')
{
g[i] = g[i-1]+1,f[i] = f[i-1]+2*g[i-1]+1;
}
else if(c =='?')
{
g[i] = 0.5*g[i-1]+0.5,f[i] = f[i-1]+g[i-1]+0.5;
// cout<<f[i]<<endl;
// cout<<i<<endl;
}
}
printf("%.4f", f[n]);
return 0;
}