您的位置:首页 > 其它

[jzoj]3468. 【NOIP2013模拟联考7】OSU!(osu) (期望DP)

2017-11-27 19:22 483 查看

Problem

给你一个序列,ai表示第i个数为1的概率,1−ai为0的概率.

在长度为n的01串中,一段连续的x个1,代价是x3,求代价期望.

Data constraint

30%的数据 n<=20

60%的数据 n<=1000

100%的数据 n<=100000

Solution

20%

这一档部分分很好拿,直接暴力即可.

60%

他喵的我想了很久,还是想不出来,我一直纠结于如何把概率给它乘上去.

但当我再想久一点后,发现自己简直是个逗比,为什么不可以用乘法分配律呢?

于是,我的想法是:

设f[i][j]表示从第i个节点开始,已经有j个连续的1了.

再设g[i][j]表示其对应的概率.

然后我们就可以通过下一个点选还是不选愉快地转移了.

#include <iostream>
#include <cstdio>
#include <cstring>

#define Maxn 100010
#define Mam 1010
#define fo(i,a,b) for (i = a; i <= b; i++)

using namespace std;

int n,i,j;

long double a[Maxn],b[Maxn],f[Mam][Mam],t[Mam][Mam];

int main()
{
freopen("osu.in","r",stdin);
freopen("osu.out","w",stdout);

scanf("%d",&n);
fo(i,1,n)
scanf("%Lf",&a[i]), b[i] = 1 - a[i]; b[i]=1;

t[0][0] = 1;
fo(i,0,n)
{
fo(j,0,i)
{
f[i+1][j+1] = (f[i+1][j+1] + f[i][j]) * a[i + 1];
t[i+1][j+1] = t[i+1][j+1] + t[i][j] * a[i + 1];
f[i+1][0] = f[i+1][0] + (j * j * j) * t[i][j] + f[i][j];
t[i+1][0] = t[i+1][0] + t[i][j] * b[i + 1];
}
f[i+1][0] = f[i+1][0] * b[i + 1];
}

printf("%0.1Lf",f[n+1][0]);
}


100%

观察上述方法,其瓶颈在于j,因为我们要求一个个数次方,所以必须知道有多少个数.

但如果,我们能不需要知道其个数,直接从i⇒i+1,那么我们就可以O(n)啦

i⇒i+1,其实我们只需要把立方给拆一下

zz都知道(i+1)3=i3+3i2+3i+1,所以我们记录f[i][1/2/3]分别表示以1∼3为次方的答案.

注意f[i][1/2]转移是必须选i+1的,而f[i][3]可以选或不选.

有点难理解.

#include <iostream>
#include <cstdio>
#include <cstring>

#define Maxn 100010
#define fo(i,a,b) for (i = a; i <= b; i++)

using namespace std;

int n,i;

long double a[Maxn],b[Maxn],f[Maxn][4];

int main()
{
freopen("osu.in","r",stdin);
freopen("osu.out","w",stdout);

scanf("%d",&n);
fo(i,1,n)
scanf("%Lf",&a[i]), b[i] = 1 - a[i]; b[i] = 1;
fo(i,0,n)
{
f[i+1][3] = (f[i][3] + 3 * f[i][2] + 3 * f[i][1] + 1) * a[i + 1];
f[i+1][3] += (f[i][3] * b[i + 1]);
f[i+1][2] = (f[i][2] + 2 * f[i][1] + 1) * a[i + 1];
f[i+1][1] = (f[i][1] + 1) * a[i + 1];
}

printf("%0.1Lf",f[n+1][3]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: