Easy [还是概率DP思想……]

题目描述

某一天\(WJMZBMR\)在打\(osu\)~~~但是他太弱逼了,有些地方完全靠运气\(QaQ\)

我们来简化一下这个游戏的规则

\(n\)次点击要做,成功了就是\(o\),失败了就是\(x\),分数是按\(comb\)计算的,连续\(a\)\(comb\)就有\(a\times a\)分,\(comb\)就是极大的连续\(o\)。比如\(ooxxxxooooxxx\),分数就是\(2\times 2+4 \times 4=4+16=20\)

\(Sevenkplus\)闲的慌就看他打了一盘,有些地方跟运气无关要么是\(o\)要么是\(x\),有些地方\(o\)或者\(x\)各有\(50\%\)的可能性,用\(?\)号来表示。比如\(oo?xx\)就是一个可能的输入。

那么\(WJMZBMR\)这场\(osu\)的期望得分是多少呢?比如\(oo?xx\)的话,\(?\)\(o\)的话就是\(oooxx >= 9\),是\(x\)的话就是\(ooxxx >= 4\) 期望自然就是\({(4+9)\over2}=6.5\)

输入格式

第一行一个整数\(n\),表示点击的个数

接下来一个字符串,每个字符都是\(ox?\)中的一个

输出格式

一行一个浮点数表示答案

四舍五入到小数点后\(4\)

如果害怕精度跪建议用\(long double\)或者\(extended\)

样例输入

4
????

样例输出

4.1250

数据范围与提示

\(n<=300000\)

\(osu\)很好玩的哦

\(WJMZBMR\)技术还行(雾),\(x\)基本上很少呢

分析

这个题和\(OSU!\)其实差别不大,也就是每个位置都有两种情况,然后就可以开始分析状态转移方程了:
首先利用两个数组\(f[i]\)\(g[i]\),一个代表前\(i\)总得分,另一个代表前\(i\)总长度。
总长度这个当然很好进行状态转移,首先是当这一位是\(o\),那么就可以直接\(g[i]=g[i-1]+1\)。其次是这一位成为了\(x\),那么\(g[i]\)就置为\(0\)。第三种就是\(?\)的情况,因为\(o\)\(x\)各为\(50\%\)的概率,所以\(g[i]=0.5\times(g[i-1]+1)+0\times 0.5\),到最后\(g[i]\)的状态转移方程就是\(g[i] ={ {g[i-1]+1}\over2}\)
其次就是期望也就是得分的状态转移的方程了:
假如这一位是\(o\)的话,那么\(f[i]\)就是相当与上一位为\(x\),这一位为\(x+1\),那么变化量也就是\(2x+1\),所以状态转移方程就是

\[f[i]=f[i−1]+2\times g[i−1]+1 \]

\[g[i]=g[i-1]+1 \]

假如这一位是\(x\)的话,那么得分也就是不变了,只需要把\(g[i]\)改成\(0\)即可,状态转移方程就是

\[f[i]=f[i−1],g[i]=0 \]

第三种就是\(?\)的情况,因为每种情况都是\(0.5\)的概率,所以状态转移方程就很好想了,就是

\[f[i]=0.5\times (f[i−1]+2\times [i−1]+1)+0.5\times f[i−1] \]

\[g[i]=0.5\times g[i−1]+1)+0.5\times 0 \]

按这三个进行状态转移就行了,下边是代码。

代码

#include
const int maxn = 3e5+10;
int n;
double f[maxn];
double g[maxn];
char ch[maxn];

int main(){
	scanf("%d ",&n);
	scanf("%s",ch+1);
	for(int i=1;i<=n;i++){
		if(ch[i]=='o'){
			f[i]=f[i-1]+2*g[i-1]+1;
			g[i]=g[i-1]+1;
		}
		else if(ch[i]=='x'){
			f[i]=f[i-1];
			g[i]=0;
		}
		else{
			g[i]=(g[i-1]+1)/2.0;
			f[i]=0.5*f[i-1]+0.5*(f[i-1]+2*g[i-1]+1);
		}
	}
	printf("%.4lf\n",f[n]);
	return 0;
}

你可能感兴趣的:(Easy [还是概率DP思想……])