P1654 OSU!

P1654 OSU!

题目

题目背景

原 《产品排序》 参见P2577

题目描述

osu 是一款群众喜闻乐见的休闲软件。

我们可以把osu的规则简化与改编成以下的样子:

一共有n次操作,每次操作只有成功与失败之分,成功对应1,失败对应0,n次操作对应为1个长度为n的01串。在这个串中连续的 X X X 1 1 1 可以贡献 X 3 X^3 X3 的分数,这x个1不能被其他连续的1所包含(也就是极长的一串1,具体见样例解释)

现在给出n,以及每个操作的成功率,请你输出期望分数,输出四舍五入后保留1位小数。

输入格式

第一行有一个正整数n,表示操作个数。接下去n行每行有一个[0,1]之间的实数,表示每个操作的成功率。

输出格式

只有一个实数,表示答案。答案四舍五入后保留1位小数。

输入输出样例

输入 #1

3 
0.5 
0.5 
0.5

输出 #1

6.0

说明/提示

【样例说明】

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

N<=100000

思路&代码

我的做法可能比较复杂,但思路可能对你有所启发.

暴力

不难想到,设 f i f_i fi表示前 i i i个操作后的期望得分,则有:
f i = ∑ j = 1 i ( ( f j − 1 + ( i − j ) 3 ) ⋅ ( ∏ k = j + 1 i p k ) ⋅ ( 1 − p j ) ) f_i=\sum_{j=1}^i\Bigg( \Big(f_{j-1}+(i-j)^3\Big)\cdot \Big(\prod^i_{k=j+1}p_k \Big)\cdot \Big(1-p_j\Big) \Bigg) fi=j=1i((fj1+(ij)3)(k=j+1ipk)(1pj))
可以做到 O ( n 2 ) O(n^2) O(n2),80分.

代码

#include <iostream>
#include <cstdio>
using namespace std;

const int N = 100010;
int n;
double p[N];
double f[N];

double pow3(double a) {
	return a * a * a;
}
int main() {
	scanf("%d" , &n);
	for(int i = 1 ; i <= n ; i++)
		scanf("%lf" , p + i);
//	f[1] = 0 * (1 - p[1]) + 1 * p[1];
	for(int i = 1 ; i <= n ; i++) {
		double P = 1;
		for(int j = i ; j >= 1 ; j--)
			f[i] += (f[j - 1] + pow3(i - j)) * P * (1 - p[j]) , P *= p[j];
		f[i] += pow3(i) * P;
	}
	printf("%.1lf" , f[n]);
	return 0;
} 

优化

我们可以考虑对上面的式子进行类似前缀和的优化,我们尝试将与 i i i有关的项从 ∑ \sum 中分离出来,便于预处理.

我们先预处理一个 p r o d i = ∏ j = 1 i p j prod_i=\prod_{j=1}^i p_j prodi=j=1ipj,这样, ∏ j = l r a j = p r o d r ÷ p r o d l − 1 \prod_{j=l}^ra_j=prod_r \div prod_{l-1} j=lraj=prodr÷prodl1.

(为了看懂下式,你需要知道 ∑ ( a ⋅ b i ) = a ⋅ ( ∑ b i ) \sum (a\cdot b_i)=a\cdot (\sum b_i) (abi)=a(bi)( a a a是一个与 i i i无关的量)).

为了简化式子,我们用 t t t代替 ( 1 − p j ) ÷ p r o d j (1-p_j)\div prod_j (1pj)÷prodj(注意 k k k j j j有关,应该放在 ∑ \sum 内).
f i = ∑ j = 1 i ( ( f j − 1 + ( i − j ) 3 ) ⋅ ( p r o d i p r o d j ) ⋅ ( 1 − p j ) ) f i = p r o d i ( ∑ j = 1 i ( f j − 1 + ( i − j ) 3 ) ⋅ t ) f i = p r o d i ( ∑ j = 1 i ( f j − 1 + i 3 − 3 i 2 j + 3 i j 2 − j 3 ) ⋅ t ) f i = p r o d i ( ( ∑ j = 1 i f j − 1 ⋅ t ) + ( ∑ j = 1 i i 3 ⋅ t ) − ( ∑ j = 1 i 3 i 2 j ⋅ t ) + ( ∑ j = 1 i 3 i j 2 ⋅ t ) − ( ∑ j = 1 i j 3 ⋅ t ) ) f i = p r o d i ( ( ∑ j = 1 i f j − 1 ⋅ t ) + i 3 ( ∑ j = 1 i t ) − 3 i 2 ( ∑ j = 1 i j ⋅ t ) + 3 i ( ∑ j = 1 i j 2 ⋅ t ) − ( ∑ j = 1 i j 3 ⋅ t ) ) \begin{aligned} f_i &= \sum_{j=1}^i\Bigg( \Big(f_{j-1}+(i-j)^3\Big)\cdot \Big(\frac{prod_i}{prod_j} \Big)\cdot \Big(1-p_j\Big) \Bigg)\\ f_i &= prod_i\Bigg( \sum^i_{j=1}\Big( f_{j-1} +(i-j)^3 \Big)\cdot t \Bigg)\\ f_i &= prod_i\Bigg( \sum^i_{j=1}\Big( f_{j-1} +i^3-3i^2j+3ij^2-j^3 \Big)\cdot t \Bigg)\\ f_i &= prod_i\Bigg( \Big(\sum^i_{j=1} f_{j-1}\cdot t\Big) +\Big(\sum^i_{j=1} i^3\cdot t \Big) -\Big(\sum^i_{j=1} 3i^2j\cdot t\Big) +\Big(\sum^i_{j=1} 3ij^2\cdot t\Big) -\Big(\sum^i_{j=1} j^3\cdot t\Big) \Bigg)\\ f_i &= prod_i\Bigg( \Big(\sum^i_{j=1} f_{j-1}\cdot t\Big) +i^3\Big(\sum^i_{j=1} t \Big) -3i^2\Big(\sum^i_{j=1} j\cdot t\Big) +3i\Big(\sum^i_{j=1} j^2\cdot t\Big) -\Big(\sum^i_{j=1} j^3\cdot t\Big) \Bigg)\\ \end{aligned} fififififi=j=1i((fj1+(ij)3)(prodjprodi)(1pj))=prodi(j=1i(fj1+(ij)3)t)=prodi(j=1i(fj1+i33i2j+3ij2j3)t)=prodi((j=1ifj1t)+(j=1ii3t)(j=1i3i2jt)+(j=1i3ij2t)(j=1ij3t))=prodi((j=1ifj1t)+i3(j=1it)3i2(j=1ijt)+3i(j=1ij2t)(j=1ij3t))
到这里, s u m sum sum内的每一项都和 i i i无关了,这就很快乐了.

我们分别令:注意 b 0 = 1 b_0=1 b0=1.
a i = ( ∑ j = 1 i f j − 1 ⋅ 1 − p i p r o d i ) b i = ( ∑ j = 1 i 1 − p i p r o d i ) c i = ( ∑ j = 1 i j ⋅ 1 − p i p r o d i ) d i = ( ∑ j = 1 i j 2 ⋅ 1 − p i p r o d i ) e i = ( ∑ j = 1 i j 3 ⋅ 1 − p i p r o d i ) \begin{aligned} a_i &=\Big(\sum^i_{j=1} f_{j-1}\cdot \frac{1-p_i}{prod_i} \Big)\\ b_i &=\Big(\sum^i_{j=1} \frac{1-p_i}{prod_i} \Big)\\ c_i &=\Big(\sum^i_{j=1} j\cdot \frac{1-p_i}{prod_i}\Big)\\ d_i &=\Big(\sum^i_{j=1} j^2\cdot \frac{1-p_i}{prod_i}\Big)\\ e_i &=\Big(\sum^i_{j=1} j^3\cdot \frac{1-p_i}{prod_i}\Big)\\ \end{aligned} aibicidiei=(j=1ifj1prodi1pi)=(j=1iprodi1pi)=(j=1ijprodi1pi)=(j=1ij2prodi1pi)=(j=1ij3prodi1pi)
则有:
f i = a i + i 3 ⋅ b i − 3 i 2 ⋅ c i + 3 i ⋅ d i − e i f_i = a_i +i^3\cdot b_i -3i^2\cdot c_i +3i\cdot d_i -e_i fi=ai+i3bi3i2ci+3idiei
这就清爽多了,可以在 O ( n ) O(n) O(n)内处理.

**为了提高精度,注意用long double.为了防止概率中出现0,我们以0为界,分开多块处理.由于块间互不影响,每一块内答案加和就是答案.**具体可以看代码.

代码

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

#define double long double
const int N = 100010;
int n;
double f[N];
double prod[N];

double a[N] , b[N] , c[N] , d[N] , e[N];

double pow3(double a) {
	return a * a * a;
}

double solve(double* p , int n) {
	prod[0] = 1;
	for(int i = 1 ; i <= n ; i++)
		prod[i] = prod[i - 1] * p[i];
	
	f[0] = 0;
	b[0] = 1;
	for(long long i = 1 ; i <= n ; i++) {
		double t = (1 - p[i]) / prod[i];
		a[i] = a[i - 1] + f[i - 1] * t;
		b[i] = b[i - 1] + t;
		c[i] = c[i - 1] + t * i;
		d[i] = d[i - 1] + t * i * i;
		e[i] = e[i - 1] + t * i * i * i;
		
		f[i] = prod[i] * (
		           a[i]
		           + b[i] * ((double)i * i * i)
		           - c[i] * ((double)3 * i * i)
		           + d[i] * ((double)3 * i)
		           - e[i]
		       );
	}
	return f[n];
}

double p[N];
int main() {
	scanf("%d" , &n);
	for(int i = 1 ; i <= n ; i++){
		scanf("%Lf" , p + i);
	}
	
	double ans = 0;
	int l = 1 , r = 1;
	
	while(l <= n) {
		r = l;
		while(r <= n && p[r] != 0)++r;
		--r;
		ans += solve(p + l - 1 , r - l + 1) ;
		l = r + 2;
	}
	printf("%.1Lf" , ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值