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

题目描述

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

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

(n)次点击要做,成功了就是(o),失败了就是(x),分数是按(comb)计算的,连续(a)(comb)就有(a imes a)分,(comb)就是极大的连续(o)。比如(ooxxxxooooxxx),分数就是(2 imes 2+4 imes 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 imes(g[i-1]+1)+0 imes 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 imes 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 imes (f[i−1]+2 imes [i−1]+1)+0.5 imes f[i−1] ]

[g[i]=0.5 imes g[i−1]+1)+0.5 imes 0 ]

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

代码

#include<bits/stdc++.h>
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
",f[n]);
	return 0;
}
原文地址:https://www.cnblogs.com/Vocanda/p/13171748.html