概率dp bzoj3450 bzoj 3418 牛客网第九场E

bzoj 3450
Description
某一天WJMZBMR在打osu~~~但是他太弱逼了,有些地方完全靠运气:(
我们来简化一下这个游戏的规则
有n次点击要做,成功了就是o,失败了就是x,分数是按comb计算的,连续a个comb就有a*a分,comb就是极大的连续o。
比如ooxxxxooooxxx,分数就是2*2+4*4=4+16=20。
Sevenkplus闲的慌就看他打了一盘,有些地方跟运气无关要么是o要么是x,有些地方o或者x各有50%的可能性,用?号来表示。
比如oo?xx就是一个可能的输入。
那么WJMZBMR这场osu的期望得分是多少呢?
比如oo?xx的话,?是o的话就是oooxx => 9,是x的话就是ooxxx => 4
期望自然就是(4+9)/2 =6.5了

Input
第一行一个整数n,表示点击的个数
接下来一个字符串,每个字符都是ox?中的一个

Output
一行一个浮点数表示答案
四舍五入到小数点后4位
如果害怕精度跪建议用long double或者extended

Sample Input
4
????
Sample Output
4.1250
n<=300000
osu很好玩的哦
WJMZBMR技术还行(雾),x基本上很少呢

思路:
f(i) 表示以第i个字符结尾的期望得分,L(i)表示以第i个字符结尾连续的o的期望长度。
分情况讨论:(设L表示当前的连续o的长度)
①当s(i)=x时,L=0,所以显然f(i)=f(i−1),L(i)=0;
②当s(i)=o时,L=L+1,得分从L2变为L2+2L+1,对答案贡献为2L+1,所以L(i)=L(i−1)+1,f(i)=f(i−1)+2∗L(i−1)+1;
③当s(i)=?时,长度L不变和变为L+1的概率都为0.5,其他的思路和上面一样。
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#define N 300001
using namespace std;
int n;
char s[N];
double f[N],d[N];
int main()
{
    scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='x')
            {f[i]=f[i-1];d[i]=0;}
        else if(s[i]=='o')
            {f[i]=f[i-1]+2*d[i-1]+1;d[i]=d[i-1]+1;}
        else {f[i]=f[i-1]+d[i-1]+0.5;d[i]=(d[i-1]+1)/2;}
    }
    printf("%.4lf",f[n]);
    return 0;
}

bzoj 3418
Description
osu 是一款群众喜闻乐见的休闲软件。
我们可以把osu的规则简化与改编成以下的样子:
一共有n次操作,每次操作只有成功与失败之分,成功对应1,失败对应0,n次操作对应为1个长度为n的01串。在这个串中连续的 X个1可以贡献X^3 的分数,这x个1不能被其他连续的1所包含(也就是极长的一串1,具体见样例解释)
现在给出n,以及每个操作的成功率,请你输出期望分数,输出四舍五入后保留1位小数。
Input
第一行有一个正整数n,表示操作个数。接下去n行每行有一个[0,1]之间的实数,表示每个操作的成功率。
Output
只有一个实数,表示答案。答案四舍五入后保留1位小数。
Sample Input
3
0.5
0.5
0.5
Sample Output
6.0
HINT
【样例说明】

000分数为0,001分数为1,010分数为1,100分数为1,101分数为2,110分数为8,011分数为8,111分数为27,总和为48,期望为48/8=6.0

N<=100000

题意:
这题跟上一个题是一个意思,只是幂次变成了a^3;
思路:
假如这个01串是确定的,考虑每新增一个位置,如果这个位置是0,则贡献为0,否则贡献为(x+1)^3−x^3=3x^2+3x+1,其中x为加入之前最长的全1后缀的长度 。
现在这个问题变成了期望问题,那么我们只需要维护一个x的期望和x2的期望即可。注意平方的期望不等于期望的平方。
可以理解为l[x]维护的是x的期望,L2[i]维护的是x^2的期望;
系数是组合数(二项式系数);
代码;

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;
int n;
double a[M],l[M],l2[M],f[M];
int main()
{
    int i;
    cin>>n;
    for(i=1;i<=n;i++)
    {
        scanf("%lf",&a[i]);
        l[i]=(l[i-1]+1)*a[i];
        l2[i]=(l2[i-1]+2*l[i-1]+1)*a[i];
        f[i]=f[i-1]+(3*l2[i-1]+3*l[i-1]+1)*a[i];
    }
    printf("%.1lf\n",f[n]);
    return 0;
}

链接:https://www.nowcoder.com/acm/contest/147/E
来源:牛客网

题目描述
Niuniu likes to play OSU!
We simplify the game OSU to the following problem.

Given n and m, there are n clicks. Each click may success or fail.
For a continuous success sequence with length X, the player can score X^m.
The probability that the i-th click success is p[i]/100.
We want to know the expectation of score.
As the result might be very large (and not integral), you only need to output the result mod 1000000007.
输入描述:
The first line contains two integers, which are n and m.
The second line contains n integers. The i-th integer is p[i].

1 <= n <= 1000
1 <= m <= 1000
0 <= p[i] <= 100
输出描述:
You should output an integer, which is the answer.
示例1
输入
复制
3 4
50 50 50
输出
复制
750000020
说明
000 0
001 1
010 1
011 16
100 1
101 2
110 16
111 81

The exact answer is (0 + 1 + 1 + 16 + 1 + 2 + 16 + 81) / 8 = 59/4.
As 750000020 * 4 mod 1000000007 = 59
You should output 750000020.
题意:
这个题求的是m次幂,再处理一下分数逆元即可,由上面两个题可知需要处理x^(m-1),x^(m-2)……x的期望,开一个L的二维数组,然后处理组合数,但是处理组合数这里,还是没搞懂,于是这个题一直没补出来,问了别人另一种做法;
如果有哪位大佬会的话,非常欢迎评论!!!!!!!!!!!萌新抱拳
另一种做法是看在一个01串里出现连续1的个数分别为(0~n)在这个串的每个位置的概率,然后累积求和;
参考博客:
https://blog.csdn.net/kuronekonano/article/details/81750309
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long int ll;
const ll mod=1e9+7;
ll ppow(ll a,ll b)//一定不要定义成pow,调了好久
{
    ll res=1;
    while(b)
    {
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll zep[1005],dp[1005][1005],p[1005],fac[1005];
//zep[i]在i这个位置出现0的概率,p[i]在i这个位置出现1的概率,fac[i]i的m次幂,dp[i][j],从i位置开始1的长度为j的期望;
ll rev=ppow(100,mod-2);//为了处理分数取模,a/b%p=a*b^(p-2)
int main()
{
    ll n,m;
    while(~scanf("%lld%lld",&n,&m))
    {
        for(int i=0; i<=n; i++)
        {
            fac[i]=ppow(i,m);
        }
        p[0]=p[n+1]=zep[0]=zep[n+1]=1;//i=0,i=n+1一定是0,p[0],p[n+1]=1,为了连乘时不影响结果,如果写成0的话,答案就都变成0了
        for(int i=1; i<=n; i++)
        {
            scanf("%lld",&p[i]);
            zep[i]=(100-p[i])*rev%mod;//预处理zep[i]在i这个位置出现0的概率的逆元
            p[i]=p[i]*rev%mod;
            dp[i][0]=1;//长度为0时概率为1,表示不对后面的概率产生影响
        }
        ll ans=0;
        for(int i=1; i<=n; i++)
        {
            for(int j=1; i+j<=n+1; j++)
            {
                dp[i][j]=dp[i][j-1]*p[i+j-1]%mod;//i+j-1的原因,长度是j,写一下就明白了
                ll tmp=dp[i][j]*zep[i-1]%mod*zep[j+i]%mod;//dp[i][j]表示,在第i个位置开始出现连续的1,
                //其长度为j的概率。即表示了一段区间内可能出现1的概率。
                //用这个结果,再乘上i-1和i+j两边的0出现概率,
                //即得到该长度的概率

                ans=(ans+tmp*fac[j]%mod)%mod;//最后乘上其权值j^m即可。
            }
        }
        ans=(ans+mod)%mod;
        printf("%lld\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/wuxiaowu547/article/details/81783608