题目描述:
简化版题意:
随机生成一个
m
+
1
m+1
m+1 个数的数列,第一个数为
0
0
0, 生成第
i
i
i 个数时,在前
i
−
1
i − 1
i−1 个数中等概率选择一个数
k
k
k, 则第
i
i
i 个数为
k
+
1
k + 1
k+1。数字
i
i
i 有一个对应的权值
a
i
a_i
ai,求数列权值和的期望。
m
≤
21
m\le21
m≤21,
m
o
d
  
998244353
\mod 998244353
mod998244353
题目分析:
(我所知的)大概有两种方法。
法一:排序差分
如果直接算期望转移的话,就要想办法表达已经选的前 i i i个数是那些数。
显然数的顺序是不重要的,我们只需要知道每种数有多少。将所得的
i
i
i个数拿来排序后,可以发现相邻两个数的差只能是0或1,所以就可以用一个
2
m
2^m
2m的状态来表示已经有的数。
设
f
[
i
]
f[i]
f[i]表示已有数字状态为
i
i
i,还能再得到的权值和的期望,答案就是
f
[
0
]
f[0]
f[0]。
可以发现我们的转移每次都是涉及到状态中的所有数,所以不必将当前拿的哪一个数放进状态中。
只需枚举当前选的数字
j
j
j,记
i
i
i中数的个数为
c
n
t
i
cnt_i
cnti,则
f
[
i
]
+
=
f
[
i
a
d
d
j
]
c
n
t
i
f[i]+=\frac {f[i~add~j]}{cnt_i}
f[i]+=cntif[i add j]
复杂度
O
(
m
⋅
2
m
)
O(m\cdot2^m)
O(m⋅2m)
Code:
#include<cstdio>
#define maxn 25
using namespace std;
const int mod = 998244353;
int n,m,a[maxn],f[1<<21],lg[1<<21],inv[maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++) scanf("%d",&a[i]);
inv[0]=inv[1]=1;
for(int i=2;i<=m;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<1<<(m-1);i++) lg[i]=lg[i>>1]+1;
for(int s=(1<<(m-1))-1;s>=1;s--){//begin shade 01, end have a 1 as barrier.
f[s]=f[s<<1]+a[0];//origin 0->1
for(int t=s,now,pre=-1,low,j=1;t;pre=now,j++){
now=lg[low=t&-t], t^=low;
if(t) f[s]=(f[s]+1ll*(f[t<<1|(s^t)]+a[j])*(now-pre))%mod;
else f[s]=(f[s]+1ll*(f[low<<1|s]+a[j])*(now-pre))%mod;
}
f[s]=1ll*f[s]*inv[lg[s]+2]%mod;
}
printf("%d\n",(f[1]+a[0])%mod);
}
法二:期望的线性性(分开算每个点的概率*权值再累加)
最后的序列是一个长度为
m
+
1
m+1
m+1的序列,设其权值和为
S
S
S
则
S
=
∑
i
=
1
m
+
1
X
i
S=\sum_{i=1}^{m+1}X_i
S=∑i=1m+1Xi,
X
i
X_i
Xi为第
i
i
i个位置的权值
那么
E
(
S
)
=
∑
i
=
1
m
+
1
E
(
X
i
)
E(S)=\sum_{i=1}^{m+1}E(X_i)
E(S)=∑i=1m+1E(Xi)
而
E
(
X
i
)
=
∑
j
=
1
i
−
1
p
[
i
]
[
j
]
∗
a
[
j
]
E(X_i)=\sum_{j=1}^{i-1}p[i][j]*a[j]
E(Xi)=∑j=1i−1p[i][j]∗a[j],
p
[
i
]
[
j
]
p[i][j]
p[i][j]为第
i
i
i个数为
j
j
j的概率。
于是只需要求出 p [ i ] [ j ] p[i][j] p[i][j]。要算这个,我们就需要知道前 i − 1 i-1 i−1个数中选了多少个 j − 1 j-1 j−1,对应就会生成多少个 j j j, p [ i ] [ j ] = E ( 生 成 的 j 的 个 数 ) i − 1 p[i][j]=\frac {E(生成的j的个数)}{i-1} p[i][j]=i−1E(生成的j的个数),同样分开统计每个位置的贡献,分子的 E = ∑ k = 1 i − 1 p [ k ] [ j − 1 ] E=\sum_{k=1}^{i-1}p[k][j-1] E=∑k=1i−1p[k][j−1]。
所以就是 p [ i ] [ j ] = 1 i − 1 ∗ ∑ k = 1 i − 1 p [ k ] [ j − 1 ] p[i][j]=\frac 1{i-1}*\sum_{k=1}^{i-1}p[k][j-1] p[i][j]=i−11∗∑k=1i−1p[k][j−1],用前缀和优化一下就可以做到 O ( m 2 ) O(m^2) O(m2)
Code:
#include<cstdio>
#define maxn 25
using namespace std;
const int mod = 998244353;
int n,m,a[maxn],p[maxn][maxn],inv[maxn],ans,sum[maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
inv[0]=inv[1]=1;
for(int i=2;i<=m;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
p[1][0]=sum[0]=1;
for(int i=2;i<=m+1;i++)
for(int j=i-1;j>=1;j--){
p[i][j]=1ll*sum[j-1]*inv[i-1]%mod,sum[j]=(sum[j]+p[i][j])%mod;
ans=(ans+1ll*p[i][j]*a[j])%mod;
}
printf("%d\n",ans);
}