传送门:B-Sample Game
题意
x x x 个数,随机到数 i i i 的概率是 p i p_i pi,现在进行以下步骤:
- 随机生成一个数
- 如果之前生成的所有数都不大于当前生成的这个数,则继续进行步骤1;
- 否则,进行分数结算并结束,若当前已经生成了 n u m num num 个数,则总分数为 n u m 2 num ^ 2 num2 。
问分数的期望。
思路
参考:【训练题43:概率dp】Sample Game | 2021牛客暑期多校训练营4
比赛的时候推出了一个大概的方程,但是没想出来怎么进行值的递推。赛后看官方题解是直接把式子推出来 O ( n ) O(n) O(n) 做的,我实在有点理解不了那个算式qwq,所以最后参考上面的链接用概率dp做了。
因为生成的序列一定是非降序的,只有最后一个数不是非降序,所以我们设 d p [ i ] dp[i] dp[i] 为数字生成到 i i i 时,最后数字总个数的期望值,设 d p 2 [ i ] dp2[i] dp2[i] 是数字生成到 i i i 时,最后数字总个数的平方的期望值,即分数的期望。
dp的转移方程推导
转移的思路为 概率*(下一个状态+贡献)
,首先我们看
d
p
dp
dp 数组的转移,若当前数字为
i
i
i ,且下一个数字
j
≥
i
j \geq i
j≥i ,那就还可以接着选择,所以此时期望为
p
j
∗
(
d
p
[
j
]
+
1
)
p_j * (dp[j] + 1)
pj∗(dp[j]+1);相反,若
j
<
i
j < i
j<i ,则没有能继续转移的状态,期望为
p
j
∗
1
p_j * 1
pj∗1。由此,我们得到
d
p
dp
dp 数组的转移方程式:
d
p
[
i
]
=
∑
j
=
1
i
−
1
p
[
j
]
+
∑
j
=
i
n
p
[
j
]
∗
(
d
p
[
j
]
+
1
)
dp[i] = \sum_{j = 1}^{i - 1} p[j] + \sum_{j = i}^n p[j] * (dp[j] + 1)
dp[i]=j=1∑i−1p[j]+j=i∑np[j]∗(dp[j]+1)
此时的左右式中均含有
d
p
[
i
]
dp[i]
dp[i] ,因此我们将右侧的移至左侧,
d
p
[
i
]
∗
(
1
−
p
[
i
]
)
=
∑
j
=
1
i
−
1
p
[
j
]
+
∑
j
=
i
+
1
n
p
[
j
]
∗
(
d
p
[
j
]
+
1
)
+
p
i
dp[i] * (1 - p[i]) = \sum_{j = 1}^{i - 1} p[j] + \sum_{j = i + 1}^n p[j] * (dp[j] + 1) + p_i
dp[i]∗(1−p[i])=j=1∑i−1p[j]+j=i+1∑np[j]∗(dp[j]+1)+pi
得到最终的转移方程式:
d
p
[
i
]
=
∑
j
=
1
i
−
1
p
[
j
]
+
∑
j
=
i
+
1
n
p
[
j
]
∗
(
d
p
[
j
]
+
1
)
+
p
i
1
−
p
[
i
]
dp[i] = \frac{\sum_{j = 1}^{i - 1} p[j] + \sum_{j = i + 1}^n p[j] * (dp[j] + 1) + p_i}{1 - p[i]}
dp[i]=1−p[i]∑j=1i−1p[j]+∑j=i+1np[j]∗(dp[j]+1)+pi
dp2的转移方程推导
之所以还需要计算 d p 2 dp2 dp2 ,是因为 E ( x 2 ) ! = E 2 ( x ) E(x^2) != E^2(x) E(x2)!=E2(x),所以我们不能简单的计算 d p dp dp 的平方。
若现在我们要得到
E
(
(
x
+
1
)
2
)
E((x + 1)^2)
E((x+1)2),则
E
(
(
x
+
1
)
2
)
=
E
(
x
2
)
+
2
E
(
x
)
+
1
E((x + 1)^2) = E(x^2) + 2E(x) + 1
E((x+1)2)=E(x2)+2E(x)+1,即
E
(
(
d
p
[
j
]
+
1
)
2
)
=
d
p
2
[
j
]
+
2
∗
d
p
[
j
]
+
1
E((dp[j] + 1) ^ 2) = dp2[j] + 2 * dp[j] + 1
E((dp[j]+1)2)=dp2[j]+2∗dp[j]+1 ,所以对于
j
≥
i
j \geq i
j≥i 的情况,此时的期望为
p
j
∗
E
(
(
d
p
[
j
]
+
1
)
2
)
=
p
j
∗
(
d
p
2
[
j
]
+
2
∗
d
p
[
j
]
+
1
)
p_j * E((dp[j] + 1)^2) = p_j * (dp2[j] + 2 * dp[j] + 1)
pj∗E((dp[j]+1)2)=pj∗(dp2[j]+2∗dp[j]+1) ;而
j
<
i
j < i
j<i 的情况,由于个数部分为1,平方后不变,所以式子一样。由此,我们得到dp2的转移方程式:
d
p
2
[
i
]
=
∑
j
=
1
i
−
1
p
[
j
]
+
∑
j
=
i
n
p
[
j
]
∗
(
d
p
2
[
j
]
+
2
d
p
[
j
]
+
1
)
dp2[i] = \sum_{j = 1}^{i - 1} p[j] + \sum_{j = i}^n p[j] * (dp2[j] + 2dp[j] + 1)
dp2[i]=j=1∑i−1p[j]+j=i∑np[j]∗(dp2[j]+2dp[j]+1)
再次将右项中的
d
p
2
[
i
]
dp2[i]
dp2[i] 移动到左侧,
d
p
2
[
i
]
∗
(
1
−
p
[
i
]
)
=
∑
j
=
1
i
−
1
p
[
j
]
+
∑
j
=
i
+
1
n
p
[
j
]
∗
(
d
p
[
j
]
+
1
)
+
2
∗
d
p
[
i
]
∗
p
i
+
p
[
i
]
dp2[i] * (1 - p[i]) = \sum_{j = 1}^{i - 1} p[j] + \sum_{j = i + 1}^n p[j] * (dp[j] + 1) + 2 * dp[i] * p_i + p[i]
dp2[i]∗(1−p[i])=j=1∑i−1p[j]+j=i+1∑np[j]∗(dp[j]+1)+2∗dp[i]∗pi+p[i]
得到最终的转移方程式:
d
p
2
[
i
]
=
∑
j
=
1
i
−
1
p
[
j
]
+
∑
j
=
i
+
1
n
p
[
j
]
∗
(
d
p
[
j
]
+
1
)
+
2
∗
d
p
[
i
]
∗
p
i
+
p
[
i
]
1
−
p
[
i
]
dp2[i] = \frac{\sum_{j = 1}^{i - 1} p[j] + \sum_{j = i + 1}^n p[j] * (dp[j] + 1) + 2 * dp[i] * p_i + p[i]}{1 - p[i]}
dp2[i]=1−p[i]∑j=1i−1p[j]+∑j=i+1np[j]∗(dp[j]+1)+2∗dp[i]∗pi+p[i]
代码实现上,先算一遍
d
p
dp
dp ,再算一遍
d
p
2
dp2
dp2 ,注意
d
p
[
i
]
dp[i]
dp[i] 的意思是已经取到
i
i
i 的情况下的长度期望,所以我们最终要求的其实是
d
p
2
[
i
+
1
]
dp2[i + 1]
dp2[i+1],所以在加入答案时,加入
d
p
2
[
i
]
+
2
∗
d
p
[
i
]
+
1
dp2[i] + 2 * dp[i] + 1
dp2[i]+2∗dp[i]+1 而非
d
p
2
[
i
]
dp2[i]
dp2[i]。
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const double eps = 1e-10;
int n;
ll p[N];
ll dp[N], dp2[N];
const int mod = 998244353;
ll qpow(ll x, ll n)
{
ll ans = 1LL;
while(n)
{
if(n & 1)
ans = (ans * x) % mod;
x = x * x % mod;
n >>= 1;
}
return ans;
}
ll inv(ll a)
{
return qpow(a, mod - 2);
}
int main()
{
cin >> n;
ll sum = 0;
for(int i = 1; i <= n; i++)
{
cin >> p[i];
sum += p[i];
}
for(int i = 1; i <= n; i++)
{
p[i] = p[i] * inv(sum) % mod;
}
for(int i = n; i > 0; i--)
{
ll fenz = p[i];
ll fenm = (1LL - p[i] + mod) % mod;
for(int j = i + 1; j <= n; j++)
{
fenz = (fenz + (p[j] * (dp[j] + 1) % mod)) % mod;
}
for(int j = 1; j < i; j++)
{
fenz = (fenz + p[j]) % mod;
}
dp[i] = fenz * inv(fenm) % mod;
}
ll ans = 0;
for(int i = n; i > 0; i--)
{
ll fenz = p[i] * ((2 * dp[i] + 1) % mod) % mod;
ll fenm = (1LL - p[i] + mod) % mod;
for(int j = i + 1; j <= n; j++)
{
fenz = (fenz + p[j] * (dp2[j] + 2LL * dp[j] % mod + 1) % mod) % mod;
}
for(int j = 1; j < i; j++)
{
fenz = (fenz + p[j]) % mod;
}
dp2[i] = fenz * inv(fenm) % mod;
ans = (ans + (dp2[i] + dp[i] * 2LL % mod + 1) % mod * p[i] % mod) % mod;
}
cout << ans << endl;
return 0;
}