序列
### Description
有一个数列\(a\),满足\(a_0=0\),\(\forall i>0\)满足\(a_i\)有\(\frac{p_i}{100}\)的概率为\(a_{i-1}+1\),有\(1-\frac{p_i}{100}\)的概率为\(0\)
求\((\sum\limits_{i=1}^n a_i)^2\)的期望
数据范围:\(1\leq n \leq 2*10^6,0\leq p_i \leq 100\)
Solution
其实真的是一道不能再小清新的概d。。。
和时机成熟之时不同,直接考虑转移的时候的变化好像并不是很方便。。(实际上也可以做但是要维护四五六七八个量就很烦还容易错==),因为并不是连续的而是一段一段的
比较简单的做法是直接看最后的形式:\((\sum\limits_{i=1}^n a_i)^2=\sum\limits_{i=1}^n a_i^2+2\sum\limits_{i=1}^n a_i\sum\limits_{j=1}^{i-1}a_j\)
所以我们求\(E( a_i^2)\)和\(E(a_i\sum\limits_{j=1}^{i-1}a_j)\)就好了
具体的话\(f_i\)表示\(E(a_i)\),\(g_i\)表示\(E(a_i^2)\),\(h_i\)表示\(E(a_i\sum\limits_{j=1}^{i-1}a_j)\),那么有(为了方便直接用\(p_i\)表示除以\(100\)之后的结果了):
\[\begin{aligned}
f_i&=p_i*(f_{i-1}+1)+(1-p_i)*0\\
g_i&=p_i*(g_{i-1}+2f_{i-1}+1)+(1-p_i)*0\\
h_i&=p_i*(h_{i-1}+(g_{i-1}+f_{i-1})\sum\limits_{j=1}^{i-2}f_j)+(1-p_i)*0\\
\end{aligned}
\]
然后就做完了
Code
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=2e6+10,MOD=998244353;
int f[N],g[N],h[N],sum[N];
int p[N];
int n,ans;
int mul(int x,int y){return 1LL*x*y%MOD;}
int plu(int x,int y){return (1LL*x+y)%MOD;}
int mns(int x,int y){return (1LL*x+MOD-y)%MOD;}
int sqr(int x){return 1LL*x*x%MOD;}
int ksm(int x,int y){
int ret=1,base=x;
for (;y;y>>=1,base=mul(base,base))
if (y&1) ret=mul(ret,base);
return ret;
}
void dp(){
f[0]=0; sum[0]=0;
for (int i=1;i<=n;++i){
f[i]=mul(p[i],f[i-1]+1);
g[i]=mul(p[i],plu(g[i-1],plu(mul(2,f[i-1]),1)));
h[i]=plu(h[i-1],plu(i>=2?sum[i-2]:0,plu(g[i-1],f[i-1])));
h[i]=mul(p[i],h[i]);
sum[i]=plu(sum[i-1],f[i]);
}
ans=0;
for (int i=1;i<=n;++i){
ans=plu(ans,g[i]);
ans=plu(ans,2LL*h[i]);
}
printf("%d\n",ans);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,inv;
inv=ksm(100,MOD-2);
scanf("%d",&n);
for (int i=1;i<=n;++i){
scanf("%d",&x);
p[i]=mul(inv,x);
}
dp();
}