题解 P1654 【OSU!】

· · 题解

这道题目最后的式子看上去是很简单的,不到10行就码完了,但是求式子的过程并没有那么简单。

很容易想到一种枚举思路: 因为每一段连续的1都有一个结束位置,我们从左到右枚举这个结束位置, 再枚举在这个位置结束的连续的1的长度,最后把贡献加入答案。

用式子写出来就是:

\sum_{i=1}^{n}\sum_{j=1}^{i}p_{i,j}j^3

其中p_{i,j}表示结束位置为i的连续1串,其长度为j的概率

如何做到$O(n)$?考虑**差分** 我们先从一个简单的问题开始 现在你有一个取值为$[1,a]$的整数随机数$x$,它取$i(1\le i\le a)$的几率为$p_i$,求$E(x)

注意这里的p和前面的有所不同

根据数学知识我们知道E(x)=\sum_{i=1}^{a}i\times p_i

现在我们把这个求和式子做一下变换:

\sum_{i=1}^{a}i\times p_i=\sum_{i=1}^{a}\sum_{j=1}^{i}p_i=\sum_{i=1}^{a}\sum_{j=i}^{a}p_j

第一个等号显然 第二个等号交换了一下求和的顺序(如果不知道为什么的可以手画一下a较小的情况)

我们记\sum_{j=i}^{a}p_j=f_i,它表示随机数的取值\ge i的概率

于是我们现在得到了另一个公式:

E(x)=\sum_{i=1}^{a}i\times p_i=\sum_{i=1}^{a}f_i

从另一个角度理解这个式子:

a=1时,显然E(x)=f_1=p_1

a=2时,我们如果继续使用f_1=p_1+p_2作为答案,会发现我们把x=2对答案的贡献给算少了;

本来应该是2\times p_2,我们的原答案(f_1)里只有一个p_2

因此我们还要加上一个f_2,即f_1+f_2

a=3时,我们如果继续使用f_1+f_2=p_1+2p_2+2p_3作为答案,会发现我们把x=3对答案的贡献给算少了;

本来应该是3\times p_3,我们的原答案(f_1+f_2)里只有两个p_3

因此我们还要加上一个f_3,即f_1+f_2+f_3 故每当x的可能取值范围扩大后,我们就需要对于原来我们给出的期望进行补足

回到这道题,我们要算的是\sum_{i=1}^{n}\sum_{j=1}^{i}p_{i,j}j^3 这里的p是前面的p_{i,j} 我们可以换成求f_{i,j},它表示结束位置为i的连续1串,其长度\ge j的概率

如果我们只要算\sum_{i=1}^{n}\sum_{j=1}^{i}p_{i,j}j(没有了立方)

那么答案变成\sum_{i=1}^{n}\sum_{j=1}^{i}f_{i,j}

而这里的f_{i,j}非常好求,就是\prod_{k=i-j+1}^{i}s_k,

因为**只要$(i-j+1)$到$i$的位置全部为$1$,那么连续1串的长度一定$\ge j$** 如果我们记$x_i=\sum_{j=1}^{i}f_{i,j}$,那么递推式就是 $$x_i=p_ix_{i-1}+p_i$$ 这就是大家喜闻乐见的第一个递推式 但是我们现在要算$Ans=\sum_{i=1}^{n}\sum_{j=1}^{i}p_{i,j}j^3

使用前面的补足思想,当x=i+1的时候,x^3需要对之前补足的贡献是(3i^2+3i+1)

因此

Ans=\sum_{i=1}^{n}\sum_{j=1}^{i}p_{i,j}j^3=\sum_{i=1}^{n}\sum_{j=1}^{i}[3(j-1)^2+3(j-1)+1]f_{i,j}

首先记y_i=\sum_{j=1}^{i}j^2p_{i,j}=\sum_{j=1}^{i}[2\times (j-1)+1]f_{i,j}

由于y_{i-1}\times p_i=\sum_{j=1}^{i-1}[2\times (j-1)+1]f_{i-1,j}\times p_i=\sum_{j=2}^{i}[2\times (j-2)+1]f_{i,j}

y_i-y_{i-1}\times p_i=\sum_{j=2}^{i}2f_{i,j}+f_{i,1}=2x_{i-1}p_i+p_i 因此y_i的递推式为

y_i=(y_{i-1}+2\times x_{i-1}+1)\times p_i

这就是大家喜闻乐见的第二个递推式

这样我们可以推到次数为3的情况,记

仿照$y_i$的方法我们有大家喜闻乐见的第三个递推式 $$dis_i=(dis_{i-1}+3\times y_{i-1}+3\times x_{i-1}+1)\times p_i$$ 使用这三个递推式即可解决问题 虽然这三个递推式并不好理解 但难道我们只是为了$AC$数而做题的吗? ```cpp #include<bits/stdc++.h> using namespace std; int n;dd a[N],x[N],y[N],dis[N]; int main() { n=read(); for(RG int i=1;i<=n;i++){ scanf("%lf",&a[i]); x[i]=(x[i-1]+1)*a[i]; y[i]=(y[i-1]+2*x[i-1]+1)*a[i]; dis[i]=dis[i-1]+(3*y[i-1]+3*x[i-1]+1)*a[i]; } printf("%.1lf\n",dis[n]); return 0; } ```