比较学习-BZOJ3450: Easy 题解 和 BZOJ4318: OSU! 题解

这两道题长得几乎一模一样,只不过easy求的是平方,OSU求的是立方
于是决定把这两题一起切掉,没想到这其中大有玄机,还是我naive啊…


我们先看easy这题
考虑期望dp;dp[i]表示考虑到第i位的时候以i结尾的连续一段的期望长度
那么如果这位是o,dp[i]=dp[i-1]+1
如果这位是x,dp[i]=0
如果这位是?,dp[i]=(dp[i-1]+1)*0.5
根据期望的线性性,考虑在维护期望长度的同时计算每一位贡献
当这位是o的时候,长度从dp[i-1]变到dp[i-1]+1,则这段比原来多贡献了 ( d p [ i 1 ] + 1 ) 2 d p [ i 1 ] 2
当这位是x的时候,没有贡献
当这位是?的时候,有 1 2 的概率长度变为dp[i-1]+1,这时的贡献是 0.5 ( ( d p [ i 1 ] + 1 ) 2 d p [ i 1 ] 2 ) ,有 1 2 的概率没有贡献
这样这题就做完了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=1e9;
const LL LINF=2e16;
const int INF=2e9;
const int magic=348;
const double eps=1e-10;
const double pi=acos(-1);

inline int getint()
{
    char ch;int res;bool f;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

int n;
char s[300048];
double ans,dp[300048];

int main ()
{
    int i;
    n=getint();scanf("%s",s+1);
    dp[0]=0;ans=0;
    for (i=1;i<=n;i++)
        if (s[i]=='o')
        {
            dp[i]=dp[i-1]+1;
            ans+=2*dp[i-1]+1;
        }
        else if (s[i]=='?')
        {
            dp[i]=(dp[i-1]+1)*0.5;
            ans+=dp[i-1]+0.5;
        }
        else
            dp[i]=0;
    printf("%.4lf\n",ans);
    return 0;
}

然后osu那题,我想以此类推的做
osu的这题相当于全是?,算期望长度的那部分是一样的,算期望贡献的那部分,有p[i]的概率长度+1,此时贡献是 p [ i ] ( ( d p [ i 1 ] + 1 ) 3 d p [ i 1 ] 3 ) ,有1-p[i]的概率长度变为0,此时没有贡献
然后这道题就做完了


然后就WA了,我感到不可理解
上网查了资料,我发现了这样一句话
长度期望的平方不等于长度平方的期望,即 E ( x 2 ) E ( x ) 2
网上还说这个东西和方差有些关系,我就大力推了一波公式
联想方差的定义
设有 n 个数 x 1 , x 2 , x 3 . . . x n
定义算术平均数 M = 1 n i = 1 n x i
则方差 D = 1 n i = 1 n ( x i M ) 2
我们尝试把式子展开

D = 1 n i = 1 n ( x i M ) 2 = 1 n i = 1 n ( x i 2 2 x i M + M 2 ) = 1 n ( i = 1 n x i 2 2 M i = 1 n x i + n M 2 ) = 1 n i = 1 n x i 2 2 M 1 n i = 1 n x i + M 2 = 1 n i = 1 n x i 2 2 M 2 + M 2 = 1 n i = 1 n x i 2 M 2

我们把 M 的定义式带回去
D = 1 n i = 1 n x i 2 ( 1 n i = 1 n x i ) 2

用人话说,就是平方和的平均数与平均数的平方的差
然后我们发现,这个东西和期望是完全等价的,我们联想离散意义下期望的定义
E ( x ) = p i x i

如果我们把上面的 x 1 , x 2 . . . x n 看做若干种情况下的收益,那么左边就是平方的期望,右边就是期望的平方,所以这两个是不等的,而且它们的差就是方差
(注意上面的给的方差的定义相当于默认所有情况出现的概率都是 1 n ,如果用加权平均数算方差就和一般的期望无异)
于是我们有了一个很牛逼的方差的定义:D=平方的期望-期望的平方


我们来考虑为什么easy套用那个方法对了,而osu就错了
我们的dp[i]维护的都是长度的期望,是一次的
在easy中, ( x + 1 ) 2 x 2 = 2 x + 1 ,我们发现他们的差实质上是一次的,所以可以直接用dp[i]相关的数据计算
在osu中, ( x + 1 ) 3 x 3 = 3 x 2 + 3 x + 1 ,这是一个二次式,我们拿三次方直接减相当于用长度的期望的平方去计算了长度平方的期望,所以结果一定会比答案小,事实上也是这样的
所以osu的正确的操作应该是用dp[i]维护长度的期望,dp2[i]维护长度的平方的期望,dp[i]的转移和上面一样,dp2[i]可以用dp2[i-1]和dp[i-1]来转移

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=1e9;
const LL LINF=2e16;
const int INF=2e9;
const int magic=348;
const double eps=1e-10;
const double pi=acos(-1);

inline int getint()
{
    char ch;int res;bool f;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

int n;double p[100048];
double ans,dp[100048],dp2[100048];

inline double tri(double x) {return x*x*x;}

int main ()
{
    int i;
    n=getint();for (i=1;i<=n;i++) scanf("%lf",&p[i]);
    dp[0]=0;dp2[0]=0;ans=0;
    for (i=1;i<=n;i++)
    {
        dp[i]=(dp[i-1]+1)*p[i];
        dp2[i]=(dp2[i-1]+2*dp[i-1]+1)*p[i];
        ans+=(3*dp2[i-1]+3*dp[i-1]+1)*p[i];
    }
    printf("%.1lf\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/iceprincess_1968/article/details/80157137