题目描述

一个o/x序列的得分为其中每个o的极大连续子段长度的平方和,比如ooxxxxooooxxx,分数就是
\(2 \times 2 + 4 \times 4 = 4 +16=20。\)
现给定一个o/x/?序列,?代表着有\(\frac 12\)的几率是o,\(\frac 12\)的几率是x,求序列的期望得分
\(n\le 3e5 + 5\)

正解:O(n)期望dp

我们假设\(f_i\)代表前i个数的期望答案,这时推导时我们发现,需要用到最后一段连续o的个数,但是迫于数据范围,这个值不能直接算,也不能枚举。这个时候我们该怎样得到这样一个值呢?这道题给了我们一个全新的思路。
对于每一位p,计算1~p - 1位连续'o'个数的期望,再用期望值计算:
\(s_p\) == 'x':E(p) = 0
\(s_p\) == 'o':E(p) = E(p - 1) + 1
\(s_p\) == '?':E(p) = \(\frac {(E(p - 1) + 1) + 0}{2}\) (E(p - 1) + 1是'?'为'o'的收益,0是'?'为'x'的收益)
将极大段的'o'拆分可得,\({(p + 1)}^2 - p^2 = 2p + 1\),对于每一个值为'o'的位置,\(f_p = f_{p - 1} + 2s_{p - 1} + 1\),对于每一个值为'?'的位置,\(f_p = f_{p - 1} + \frac {2s_{p - 1} + 1}2\)
一句话概括:当前位收益E(2p + 1) = 2E(p) + 1

Code

#include<bits/stdc++.h>
using namespace std;
string s;
double f[300005];
int n;
int main()
{
	double len = 0;
	cin>>n;
	cin>>s;
	f[0] = 0;
	for(int i = 0;i < s.length();i++)
	{
		if(s[i] == 'o')
		{
			f[i + 1] = f[i] + len * 2 + 1;
			len++;
		}
		if(s[i] == 'x')
		{
			f[i + 1] = f[i];
			len = 0;
		}
		if(s[i] == '?')
		{
			f[i + 1] = f[i] + len + 0.5;
			len = (len + 1) / 2;
		}
	}
	cout<<fixed<<setprecision(4)<<f[n];
	return 0;
}