题目
题目背景
原 《产品排序》 参见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=1∑i((fj−1+(i−j)3)⋅(k=j+1∏ipk)⋅(1−pj))
可以做到
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÷prodl−1.
(为了看懂下式,你需要知道 ∑ ( a ⋅ b i ) = a ⋅ ( ∑ b i ) \sum (a\cdot b_i)=a\cdot (\sum b_i) ∑(a⋅bi)=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
(1−pj)÷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=1∑i((fj−1+(i−j)3)⋅(prodjprodi)⋅(1−pj))=prodi(j=1∑i(fj−1+(i−j)3)⋅t)=prodi(j=1∑i(fj−1+i3−3i2j+3ij2−j3)⋅t)=prodi((j=1∑ifj−1⋅t)+(j=1∑ii3⋅t)−(j=1∑i3i2j⋅t)+(j=1∑i3ij2⋅t)−(j=1∑ij3⋅t))=prodi((j=1∑ifj−1⋅t)+i3(j=1∑it)−3i2(j=1∑ij⋅t)+3i(j=1∑ij2⋅t)−(j=1∑ij3⋅t))
到这里,
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=1∑ifj−1⋅prodi1−pi)=(j=1∑iprodi1−pi)=(j=1∑ij⋅prodi1−pi)=(j=1∑ij2⋅prodi1−pi)=(j=1∑ij3⋅prodi1−pi)
则有:
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+i3⋅bi−3i2⋅ci+3i⋅di−ei
这就清爽多了,可以在
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;
}