一.基本概念
1.随机变量
一个随机实验的某种可能结果被称为样本点,样本点的集合为样本空间
例:掷骰子实验,一种可能的结果为掷出点数3,我们以{3}表示,则{3}为一个样本点,{1,2,3,4,5,6}为样本空间
随机事件为样本空间的某个子集
例:掷骰子实验,问掷出的点数大于3的概率是多少?掷出的点数大于3就是一个随机事件,其本质是样本点的集合——{4,5,6}
随机变量是将样本点映射为数的一个函数
在上面的例子中,我将掷骰子实验的结果(即样本点)映射为了掷出的点数,实际上就是随机变量。
例1:某个公交车站等车的人数是一个不断在变化的数量,可以理解为一个随机试验,那么就可以很方便的用{5}表示有5个人在等车、{7}表示7个人在等车——将实验结果用数来表达。
例2:掷骰子实验、车站等车人数的样本点本身就有很明显的数量特征,其实一些非数量特征也可以用数来表达。比如在统计人口中的性别比例时,用1表示男性,0表示女性。
2.期望
用 X X X表示随机变量
以下讨论的主要是离散型随机变量,即随机变量的取值为可列的
X X X的取值有 x 1 、 x 2 、 x 3 … … x_1、x_2、x_3…… x1、x2、x3……
x i x_i xi的概率为 P ( X = x i ) = p i P(X=x_i)=p_i P(X=xi)=pi
则随机变量 X X X的期望为 E ( X ) = ∑ p i x i E(X)=\sum p_ix_i E(X)=∑pixi,即随机变量的期望为随机变量取值与对应概率的乘积之和
例:掷一颗骰子得到的点数的期望为 1 + 2 + 3 + 4 + 5 + 6 6 = 3.5 \frac{1+2+3+4+5+6}{6}=3.5 61+2+3+4+5+6=3.5
性质: E ( a X + b Y ) = a ∗ E ( X ) + b ∗ E ( Y ) E(aX+bY)=a*E(X)+b*E(Y) E(aX+bY)=a∗E(X)+b∗E(Y)
X 、 Y X、Y X、Y表示两个不同的随机变量
例:仍然以掷骰子为例, X X X表示掷一颗6面的骰子得到的点数, Y Y Y表示掷一颗12面的骰子得到的点数,则 X + Y X+Y X+Y就表示各掷一颗12面的骰子和6面的骰子得到的点数之和, a ∗ X a*X a∗X表示掷一颗6面的骰子a次得到的点数之和
如果按照定义去计算 E ( a X ) E(aX) E(aX)会比较麻烦,但是 E ( X ) E(X) E(X)很好算,所以我们利用期望的线性,很快就能计算出 E ( a x ) E(ax) E(ax)的值
例:掷两颗骰子得到的点数之和的期望为 2 ∗ E ( X ) = 7 2*E(X)=7 2∗E(X)=7
二.例题
下面将结合例题来学习知识点:
- 期望的线性性质
- 根据定义求期望
- 期望dp
- 概率dp
可在vjudge上找到对应的题目
1.Crossing Rivers UVA - 12230
题意:
村庄A与村庄B的距离为D,二者之间有n条河。若以点A为原点,A->B方向为正方向建立坐标轴,可将河视为坐标轴上的线段。每条河长度为 L i L_i Li,河的左端与点A的距离为 p i p_i pi,河上有一条船速度为 v i v_i vi,在河的左右端间不断往返运动
你在陆地上的行走速度为1,到达河岸时如果船没靠岸则只能等待。当你从点A出发时,每条河上船的初始位置与朝向都是随机的,问从A运动到B的期望时间为多少
分析:
根据期望的线性性质,从A运动到B的期望时间为陆地上运动的期望时间加上过每条河所耗费的期望时间,其中前者为常数 D − ∑ L i D-\sum L_i D−∑Li。
过一条河所需的时间与到达河岸时,船的位置和朝向有关,而这两点都是随机的。最坏情况下,到河岸时船正好离岸,则过河耗时 3 ∗ L i v i \frac{3*L_i}{v_i} vi3∗Li;最好情况下,到河岸时船正好到岸,过河耗时 L i v i \frac{L_i}{v_i} viLi。
最坏情况与最好情况之间其实有无数种情况,所以过河所需时间是一个连续型的随机变量,由题目可得,到达河岸时船的位置和朝向是随机等概率分布的,故可知过河时间满足均匀分布,期望为 L i v i + 3 ∗ L i v i 2 = 2 ∗ L i v i \frac{\frac{L_i}{v_i}+\frac{3*L_i}{v_i}}{2}=\frac{2*L_i}{v_i} 2viLi+vi3∗Li=vi2∗Li
最终答案为 ∑ 2 ∗ L i v i + D − ∑ L i \sum\frac{2*L_i}{v_i}+D-\sum L_i ∑vi2∗Li+D−∑Li
这道题的思路是期望的线性性质,上题中点A到点B这段路可以被分为若干条河与若干段陆地,相当于将随机变量“点A到点B所需时间”拆分为了若干个小的随机变量,对每一部分单独求期望后再求和即得原随机变量的期望值
E ( X 1 + X 2 + ⋅ ⋅ ⋅ + X n ) = E ( X 1 ) + E ( X 2 ) + ⋅ ⋅ ⋅ E ( X n ) E(X_1+X_2+\cdot\cdot\cdot+X_n)=E(X_1)+E(X_2)+\cdot\cdot\cdot E(X_n) E(X1+X2+⋅⋅⋅+Xn)=E(X1)+E(X2)+⋅⋅⋅E(Xn)
注:常数 C C C的期望为 E ( C ) = C E(C)=C E(C)=C,陆地上的耗时为常数
代码:
int n,D;
int Case=1;
while(~scanf("%d%d",&n,&D)){
if(!n&&!D) break;
double ans=0,sum=0;
for(int i=0;i<n;i++){
int p,L,v;
scanf("%d%d%d",&p,&L,&v);
sum+=L*1.0;
ans+=(2.0*L/v);
}
ans+=(D-sum);
printf("Case %d: %.3f\n\n",Case++,ans);
}
2.Candy UVA - 1639
题意:
有两盒糖果,每盒都有n颗糖果,小明每天随机选一个盒子打开,从中拿出一颗糖果吃。选盒子1的概率是p,选盒子2的概率是1-p。问:当小明打开盒子,发现盒子是空的时,另一个盒子中糖果数量的期望值
n<=2e5
分析:
先分析随机变量,另一个盒子中的糖果数量可能为0~n,故为离散型随机变量,因此只需要计算出对应的概率值,就可以依照定义计算出期望值
题目分为两种情况:
- 打开盒子1发现是空的
- 打开盒子2发现是空的
先讨论第1种情况下,盒子2中还剩下 i i i颗糖果的概率,一共取了 2 n − i 2n-i 2n−i次糖果,其中 n n n次取了盒子1中的糖果。故概率为 p ∗ C ( 2 n − i , n ) ∗ p n ∗ ( 1 − p ) n − i p*C(2n-i,n)*p^n*(1-p)^{n-i} p∗C(2n−i,n)∗pn∗(1−p)n−i
这里需要多乘一个 p p p,是最后一次打开盒子1的概率。因为由题意得,取完盒子的最后一颗糖果后,是无法得知盒子已经空了的,只有打开一个空的盒子才能发现。故相当于求 2 n − i + 1 2n-i+1 2n−i+1开盒子,其中最后一次确定开盒子1,前面 2 n − i 2n-i 2n−i次中 n n n次开盒子1的概率
如果无法理解,可以先研究一下二项式分布
第2种情况: C ( 2 n − i , n ) ∗ ( 1 − p ) n + 1 ∗ ( p ) n − i C(2n-i,n)*(1-p)^{n+1}*(p)^{n-i} C(2n−i,n)∗(1−p)n+1∗(p)n−i
推出公式后,还有一个难点就是数据处理,直接求 C ( 2 n − i , n ) ∗ p n + 1 ∗ ( 1 − p ) n − i C(2n-i,n)*p^{n+1}*(1-p)^{n-i} C(2n−i,n)∗pn+1∗(1−p)n−i是不可能的,因为后两个部分 p n + 1 p^{n+1} pn+1和 ( 1 − p ) n − i (1-p)^{n-i} (1−p)n−i值太小了,而 C ( 2 n − i , n ) C(2n-i,n) C(2n−i,n)又太大了,超出了基本数据类型的范围从而无法运算
一个解决方案是,对每个部分的值取对数,再进行运算,最后再幂运算得到答案
组合数的对数值可以进行预处理:
l n ( C ( n , m ) ) = l n ( n ! m ! ( n − m ) ! ) = l n ( n ! ) − l n ( m ! ) − l n ( ( n − m ) ! ) ln(C(n,m))=ln(\frac{n!}{m!(n-m)!})=ln(n!)-ln(m!)-ln((n-m)!) ln(C(n,m))=ln(m!(n−m)!n!)=ln(n!)−ln(m!)−ln((n−m)!)
阶乘的对数值可以递推求得:
l n ( x ! ) = l n ( x ) + l n ( ( x − 1 ) ! ) ln(x!)=ln(x)+ln((x-1)!) ln(x!)=ln(x)+ln((x−1)!)
利用对数的性质:
l n ( p n + 1 ) = ( n + 1 ) ∗ l n ( p ) ln(p^{n+1})=(n+1)*ln(p) ln(pn+1)=(n+1)∗ln(p)
最后三个部分的值求和,再用 e x p exp exp函数进行处理
取对数是一个很好的压缩信息的手段√
这题就是一个比较标准的求离散型变量期望的题目,先求随机变量每个取值的概率,再和取值相乘后求和
代码:
long double fact[400005];
void _pre(){
fact[1]=0;
for(int i=2;i<=400000;i++)
fact[i]=fact[i-1]+log(i);
}
int main(){
_pre();
int n,Case=1;
long double p;
while(~scanf("%d%Lf",&n,&p)){
long double ans=0;
for(int i=1;i<=n;i++){
long double t1=0,t2=0;
t1+=(n+1)*log(p),t1+=(n-i)*log(1-p);
t1+=(fact[2*n-i]-fact[n]-fact[n-i]);
t2+=(n+1)*log(1-p),t2+=(n-i)*log(p);
t2+=(fact[2*n-i]-fact[n]-fact[n-i]);
ans+=(exp(t1)+exp(t2))*i;
}
printf("Case %d: %.6Lf\n",Case++,ans);
}
return 0;
}
3.Coupons UVA - 10288
题意:
一个箱子中有一张优惠券,共有n种优惠券,每种优惠券出现的概率相同,问集齐所有优惠券,所需的开箱次数的期望值
分析:
本题介绍两种思路:
首先,开箱次数是一个离散型随机变量,可能的取值为 n 、 n + 1 、 n + 2 ⋅ ⋅ ⋅ n、n+1、n+2\cdot\cdot\cdot n、n+1、n+2⋅⋅⋅,它的取值有无限多个,并且很难去像上一题一样去计算出每种取值的概率。
类似第1题,将抽到n种优惠券所需次数进行拆分,讨论在已经获得 k k k种优惠券的情况下,得到一个新的优惠券所需次数的期望值:
每次开箱是独立的,有 k n \frac{k}{n} nk的概率抽到已有的优惠券, 1 − k n 1-\frac{k}{n} 1−nk的概率抽到新的优惠券,则用 t t t次抽到新优惠券的概率是 ( k n ) t − 1 ⋅ ( 1 − k n ) (\frac{k}{n})^{t-1}\cdot(1-\frac{k}{n}) (nk)t−1⋅(1−nk),抽到新优惠券所需次数的期望值为 ( 1 − k n ) ( 1 + 2 ∗ k n + 3 ∗ k 2 n 2 + 4 ∗ k 3 n 3 + ⋅ ⋅ ⋅ ) (1-\frac{k}{n})(1+2*\frac{k}{n}+3*\frac{k^2}{n^2}+4*\frac{k^3}{n^3}+\cdot\cdot\cdot) (1−nk)(1+2∗nk+3∗n2k2+4∗n3k3+⋅⋅⋅)
利用错位相减,可以得到该期望值为 1 + k n + k 2 n 2 + k 3 n 3 + ⋅ ⋅ ⋅ 1+\frac{k}{n}+\frac{k^2}{n^2}+\frac{k^3}{n^3}+\cdot\cdot\cdot 1+nk+n2k2+n3k3+⋅⋅⋅,由等比数列求和公式得 a 1 ( 1 − q t ) 1 − q \frac{a_1(1-q^t)}{1-q} 1−qa1(1−qt),由于 q q q小于1,当 t t t趋于无穷时该值为 n n − k \frac{n}{n-k} n−kn
如果熟知几何分布,则不需要这么麻烦的推导,几何分布的期望值为概率的倒数
综上,总次数为 n ∗ ( 1 n − 0 + 1 n − 1 + ⋅ ⋅ ⋅ + 1 1 ) n*(\frac{1}{n-0}+\frac{1}{n-1}+\cdot\cdot\cdot+\frac{1}{1}) n∗(n−01+n−11+⋅⋅⋅+11)
还有一种更为通用的做法:期望DP
令 d p [ k ] dp[k] dp[k]表示还差 k k k种优惠券没收集时,距离收集完全所需次数的期望值
d p [ k ] = k n ( d p [ k − 1 ] + 1 ) + n − k n ( d p [ k ] + 1 ) dp[k]=\frac{k}{n}(dp[k-1]+1)+\frac{n-k}{n}(dp[k]+1) dp[k]=nk(dp[k−1]+1)+nn−k(dp[k]+1)
理解:某一状态的期望值等价于,其所有(后续状态的期望值加上状态转移的代价)乘上后续状态发生的概率之和
移项后得 d p [ i ] = d p [ i − 1 ] + n i dp[i]=dp[i-1]+\frac{n}{i} dp[i]=dp[i−1]+in
易得 d p [ 0 ] = 0 dp[0]=0 dp[0]=0,递推即得答案
不难发现这个式子和第一种做法得出的式子本质上是一样的
代码:
struct frac{
long long num; //numerator
long long den; //denominator
};
long long gcd(long long a,long long b){
return a%b==0?b:gcd(b,a%b);
}
long long lcm(long long a,long long b){
return a/gcd(a,b)*b;
}
frac operator + (frac a,frac b){
long long l=lcm(a.den,b.den);
a.num*=(l/a.den),b.num*=(l/b.den);
long long g=gcd(a.num+b.num,l);
return frac{(a.num+b.num)/g,l/g};
}
long long len(long long t){ //判断位数
long long ret=0;
while(t)
t/=10,ret++;
return ret;
}
int main(){
int n;
while(~scanf("%d",&n)){
frac ans=frac{0,1};
for(int k=0;k<n;k++)
ans=ans+frac{n,n-k};
if(ans.den==1) printf("%d\n",ans.num);
else {
int temp=max(len(ans.num%ans.den),len(ans.den));
for(int i=0;i<len(ans.num/ans.den)+1;i++) printf(" ");
printf("%lld\n",ans.num%ans.den);
printf("%lld ",ans.num/ans.den);
for(int i=0;i<temp;i++) printf("-");
printf("\n");
for(int i=0;i<len(ans.num/ans.den)+1;i++) printf(" ");
printf("%lld\n",ans.den);
}
}
return 0;
}
4.Expect the Expected UVA - 11427
题意:
你每天都玩一种游戏,每一局赢的概率为 p p p,输的概率为 1 − p 1-p 1−p。你每天最多玩 n n n局游戏,当你一天游戏的胜率超过 p p p时,你就不玩了,第二天再继续玩;当你一天玩了 n n n局游戏后胜率仍然小于等于 p p p,你就再也不玩这游戏了,问你能玩这个游戏天数的期望值
分析:
每一天都是独立的,若某一天玩了 n n n局后胜率小于等于 p p p,即放弃游戏的概率为Q,则玩游戏天数显然符合几何分布,期望值为 1 Q \frac{1}{Q} Q1
设 d p [ i ] [ j ] dp[i][j] dp[i][j]为玩了 i i i局,赢了 j j j局的概率
则放弃游戏的情况: Q = ∑ i = 0 i / n < = p d p [ n ] [ i ] Q=\sum_{i=0}^{i/n<=p}dp[n][i] Q=∑i=0i/n<=pdp[n][i]
概率递推式: d p [ i ] [ j ] = ( 1 − p ) ∗ d p [ i − 1 ] [ j ] + p ∗ d p [ i − 1 ] [ j − 1 ] dp[i][j]=(1-p)*dp[i-1][j]+p*dp[i-1][j-1] dp[i][j]=(1−p)∗dp[i−1][j]+p∗dp[i−1][j−1]
若 j / i > p j/i>p j/i>p,则会结束游戏,后面的dp值无法通过当前状态转移得到,故不妨令 d p [ i ] [ j ] = 0 dp[i][j]=0 dp[i][j]=0
这种通过递推求得概率的方法被称为概率DP
代码:
long double dp[105][105];
int main(){
int N;
scanf("%d",&N);
for(int Case=1;Case<=N;Case++){
int a,b,n;
scanf("%d/%d %d",&a,&b,&n);
double p=a*1.0/b;
memset(dp,0,sizeof(dp));
dp[0][0]=1.0;
for(int i=1;i<=n;i++)
for(int j=0;j<=i*p;j++)
j?dp[i][j]=(1.0-p)*dp[i-1][j]+p*dp[i-1][j-1]:dp[i][j]=dp[i-1][j]*(1-p);
double Q=0;
for(int i=0;i<=n*p;i++)
Q+=dp[n][i];
printf("Case #%d: %d\n",Case,(int)(1.0/Q));
}
return 0;
}
三.刷题
1.LOOPS HDU - 3853
题意:
R行C列的迷宫,在每个点会随机做出选择:向下、向右或者保持不动。每次选择需要消耗2点魔力值,问从左上角出发到右下角需要消耗的魔力值的期望值为多少?
分析:
简单期望dp, d p [ i ] [ j ] dp[i][j] dp[i][j]表示点 ( i , j ) (i,j) (i,j)到点 ( R , C ) (R,C) (R,C)所需魔力的期望值,则 d p [ i ] [ j ] = p 1 ∗ d p [ i ] [ j ] + p 2 ∗ d p [ i ] [ j + 1 ] + p 3 ∗ d p [ i + 1 ] [ j ] dp[i][j]=p_1*dp[i][j]+p_2*dp[i][j+1]+p_3*dp[i+1][j] dp[i][j]=p1∗dp[i][j]+p2∗dp[i][j+1]+p3∗dp[i+1][j],其中 p 1 、 p 2 、 p 3 p_1、p_2、p_3 p1、p2、p3分别表示保持不动、向右、向下的概率
原理在第3道例题中已经讲到:
某一状态的期望值等价于,其所有(后续状态的期望值加上状态转移的代价)乘上后续状态发生的概率之和
代码:
double dp[1005][1005];
double mp[1005][1005][3];
int main(){
int R,C;
while(~scanf("%d%d",&R,&C)){
memset(dp,0,sizeof(dp));
for(int i=1;i<=R;i++)
for(int j=1;j<=C;j++)
scanf(" %lf %lf %lf",&mp[i][j][0],&mp[i][j][1],&mp[i][j][2]);
for(int i=R;i>=1;i--)
for(int j=C;j>=1;j--){
if(i==R&&j==C) dp[i][j]=0;
if(mp[i][j][0]==1.0) continue;
dp[i][j]=(mp[i][j][1]*dp[i][j+1]+mp[i][j][2]*dp[i+1][j]+2.0)/(1-mp[i][j][0]);
}
printf("%.3f\n",dp[1][1]);
}
return 0;
}
2.Aeroplane chess HDU - 4405
题意:
N+1个格子编号0~N,以0为起点,大于等于N的格子处为终点。每轮掷一个骰子,以掷出的点数决定前进的格子数。存在一些特殊的格子 X i X_i Xi可以直接跳跃到 Y i ( Y I > X i ) Y_i(Y_I>X_i) Yi(YI>Xi)。
问从起点到终点掷骰子次数的期望值为多少
分析:
还是期望dp,倒序推出答案
代码:
int airline[100010];
double dp[100010];
int main(){
int N,M;
while(~scanf("%d%d",&N,&M)){
if(!N&&!M) break;
memset(dp,0,sizeof(dp));
memset(airline,-1,sizeof(airline));
for(int i=0;i<M;i++){
int x,y;
scanf("%d%d",&x,&y);
airline[x]=y;
}
for(int i=N-1;i>=0;i--){
if(airline[i]!=-1) {dp[i]=dp[airline[i]];continue;}
for(int j=1;j<=6;j++)
dp[i]+=1.0/6*(dp[i+j]+1);
}
printf("%.4f\n",dp[0]);
}
return 0;
}
3.Collecting Bugs POJ - 2096
题意:
有n种bug,s个系统,每天会随机出现一种bug,随机出现在某个系统中。问每个系统都出现过n种bug所需天数的期望值为多少?
分析:
期望DP
d p [ i ] [ j ] dp[i][j] dp[i][j]表示距离达成 n n n种bug, s s s个系统的目标所需天数的期望值
d p [ i ] [ j ] dp[i][j] dp[i][j]后续有四种状态:
- ①旧系统出现旧bug
- ②旧系统出现新bug
- ③新系统出现旧bug
- ④新系统出现新bug
概率分别为 p 1 p_1 p1、 p 2 p_2 p2、 p 3 p_3 p3、 p 4 p_4 p4
d p [ i ] [ j ] = p 1 ∗ d p [ i ] [ j ] + p 2 ∗ d p [ i + 1 ] [ j ] + p 3 ∗ d p [ i ] [ j + 1 ] + p 4 ∗ d p [ i + 1 ] [ j + 1 ] dp[i][j]=p_1*dp[i][j]+p_2*dp[i+1][j]+p3*dp[i][j+1]+p4*dp[i+1][j+1] dp[i][j]=p1∗dp[i][j]+p2∗dp[i+1][j]+p3∗dp[i][j+1]+p4∗dp[i+1][j+1]
代码:
double dp[1005][1005];
int main(){
int n,s;
scanf("%d%d",&n,&s);
for(int i=n;i>=0;i--)
for(int j=s;j>=0;j--){
if(i==n&&j==s) continue;
double p1=1.0*i/n*j/s;
double p2=1.0*(n-i)/n*j/s;
double p3=1.0*i/n*(s-j)/s;
double p4=1.0*(n-i)/n*(s-j)/s;
dp[i][j]=(p2*dp[i+1][j]+p3*dp[i][j+1]+p4*dp[i+1][j+1]+1)/(1-p1);
}
printf("%.4f\n",dp[0][0]);
return
4.Sample Game
题目链接:Sample Game
题意:
每一轮从1到n中选一个数,选中数字 i i i的概率为 p i p_i pi,已选中的数构成了集合 S S S,若 i i i为 S S S中最大的数,则继续选数,否则计算得分: ∣ S ∣ 2 |S|^2 ∣S∣2
问得分期望值为多少
分析:
d p [ x ] dp[x] dp[x]表示当前集合最大的数为 x x x,还能选数的回合数的期望值。
d p [ x ] = ∑ i = x n p i ∗ ( d p [ i ] + 1 ) + ∑ i = 1 x − 1 p i ∗ 1 dp[x]=\sum_{i=x}^np_i*(dp[i]+1)+\sum_{i=1}^{x-1}p_i*1 dp[x]=∑i=xnpi∗(dp[i]+1)+∑i=1x−1pi∗1
( 1 − p x ) d p [ x ] = 1 + ∑ i = x + 1 n p i ∗ d p [ i ] (1-p_x)dp[x]=1+\sum_{i=x+1}^np_i*dp[i] (1−px)dp[x]=1+∑i=x+1npi∗dp[i]
d p [ x ] = 1 + ∑ i = x + 1 n p i ∗ d p [ i ] 1 − p x dp[x]=\frac{1+\sum_{i=x+1}^np_i*dp[i]}{1-p_x} dp[x]=1−px1+∑i=x+1npi∗dp[i]
令 d p 2 [ x ] dp2[x] dp2[x]表示当前集合最大的数为 x x x,得分的期望值
E ( ( x + 1 ) 2 ) = E ( x 2 + 2 x + 1 ) E((x+1)^2)=E(x^2+2x+1) E((x+1)2)=E(x2+2x+1)
则 d p 2 [ x ] = ∑ i = x n p i ∗ ( d p 2 [ i ] + 2 ∗ d p [ i ] + 1 ) + ∑ i = 1 x − 1 p i ∗ 1 dp2[x]=\sum_{i=x}^np_i*(dp2[i]+2*dp[i]+1)+\sum_{i=1}^{x-1}p_i*1 dp2[x]=∑i=xnpi∗(dp2[i]+2∗dp[i]+1)+∑i=1x−1pi∗1
d p 2 [ x ] = 1 + 2 ∗ p i ∗ d p [ i ] + ∑ i = x + 1 n p i ∗ ( d p 2 [ i ] + 2 ∗ d p [ i ] ) 1 − p i dp2[x]=\frac{1+2*p_i*dp[i]+\sum_{i=x+1}^np_i*(dp2[i]+2*dp[i])}{1-p_i} dp2[x]=1−pi1+2∗pi∗dp[i]+∑i=x+1npi∗(dp2[i]+2∗dp[i])
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const long long mod=998244353;
long long p[105];
long long dp[105];
long long dp2[105];
long long quickpow(long long base,long long power){
long long ret=1;
while(power){
if(power&1) ret=ret*base%mod;
base=base*base%mod;
power>>=1;
}
return ret;
}
long long inv(long long x){
return quickpow(x,mod-2)%mod;
}
int main(){
int n;
long long ans,sum,temp;
scanf("%d",&n);
temp=sum=0;
for(int i=1;i<=n;i++) {scanf("%lld",&p[i]),sum+=p[i];}
for(int i=1;i<=n;i++) p[i]*=quickpow(sum,mod-2),p[i]%=mod;
for(int i=n;i>=0;i--){
dp[i]=(1+temp)%mod*quickpow(1+mod-p[i],mod-2)%mod;
temp+=(p[i]*dp[i]),temp%=mod;
}
temp=0;
for(int i=n;i>=0;i--){
dp2[i]=(1+2*p[i]*dp[i]%mod+temp)%mod*quickpow(1+mod-p[i],mod-2)%mod;
temp+=(+p[i]*(dp2[i]+2*dp[i])),temp%=mod;
}
printf("%lld\n",dp2[0]);
return 0;
}