概率dp

题目链接:题目传送门

 

概率dp刷的比较少,这个题看了好几天看不懂,真的是捉急啊

 

题目描述

给你一个长 n 的序列,m 次查询

每次查询给一个 x,然后:

从序列的最左端 1 开始,每次随机的选择一个右端点 r,如果两个端点间的区间和不超过 x ,就进行一次分割,然后把左端点变成 r + 1, 否则一直随机下去。

问这样分割出来的期望段数

输入描述:

第一行两个数 n,m
之后一行 n 个数表示这个序列
之后m行每行一个数 x,表示求每段的和不大于 x 的情况下切割的期望段数

输出描述:

m 行,每行一个保留到小数点后2位的数表示答案
如果无论如何都没有合法的切割方案,输出"YNOI is good OI!",不含引号
示例1

输入

5 6
1 2 3 4 5
4
5
6
7
8
9

输出

YNOI is good OI!
4.25
3.83
3.58
3.58
3.11

说明

对于30%的数据,n , m <= 10
对于60%的数据,n <= 1000 , m <= 10
对于80%的数据,n <= 100000 , m <= 100
对于100%的数据,n <= 100000 , m <= 500 , 0 <= a[i] <= 1000 , x <= 1000000
PS:数据直接随的,不知道有没有乱搞方法~

思路:

 

 

求出 i 位置的断开的概率 pi,那么答案就是 ans=∑ni=1pi 
假设知道了 pi , 同时也可以知道这个位置之后最多连续有多少个元素和小于 x 的的元素个数 inum,那么这位对 [i+1,i+2,…,i+inum] 贡献 p iinum 
那么对于每一位

pi=∑k<i≤k+knumpkknum

 

尺取 O(n)

代码:

 

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <queue>
#define ri(n) scanf("%d",&n)
#define oi(n) printf("%d\n",n)
#define rl(n) scanf("%lld",&n)
#define ol(n) printf("%lld\n",n)
#define rep(i,l,r) for(i=l;i<=r;i++)
#define rep1(i,l,r) for(i=l;i<r;i++)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int epg=10-8;
const int maxn=1e5+10;
int a[maxn];
double o[maxn];
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)==2)
    {
        int ma=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            ma=max(ma,a[i]);
        }

        while(m--)
        {
            int x;
            scanf("%d",&x);
            if(x<ma)
            {
                printf("YNOI is good OI!\n");
                continue;
            }
            else
            {
                for(int i=0;i<=n;i++)
                    o[i]=0;
                double s=0,p=0,pp=1.0;
                double ans=0;
                int low=1,top=0;
                while(low<=n)
                {
                    if(s<=x&&top<=n)
                        s+=a[++top];
                    else
                    {
                        double t=pp/(top-low);
                        o[top]-=t;
                        s-=a[low];
                        p+=o[low++]+t;
                        ans+=pp=p;
                    }
                }
                printf("%.2f\n",ans);
            }
        }

    }
    return 0;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值