题目大意:
你有 N N N 个骰子,和一个序列 A i A_i Ai, 第 i i i 个骰子会等概率要到点数 1 ∼ A i 1 ∼ A_i 1∼Ai, 在同时抛 N N N 个骰子后,选出一些骰子,使得总点数是 10 10 10 的概率是多少,对 998 , 244 , 353 998,244,353 998,244,353 取模。
解决方法:
求概率,很明显的概率dp。
第一步, 应该设什么样的状态
一共
N
N
N 个骰子,每个骰子扔出后会对是否能抛出
1
~
10
1 ~ 10
1~10 产生影响,所以考虑每抛出一个骰子,能否抛出
1
~
10
1 ~ 10
1~10, 怎么表示呢?
数值很小是
10
10
10, 每个点数只有两种状态:能和不能,那么就可以用状态压缩。
状态即为:
f
[
i
]
[
S
]
f[i][S]
f[i][S] 表示跑到第
i
i
i 个骰子的时候,可以抛出
x
x
x 点
(
x
∈
S
)
(x \in S)
(x∈S)。
第二步,初值怎么设
如果不抛,那么在 S S S 的第一位为 1 1 1 , 则 f [ 0 ] [ 1 ] = 1 f[0][1] = 1 f[0][1]=1
第三步,转移方程是什么
假设上一步的状态是
k
k
k, 这一个骰子投出
j
j
j 时,如果选,那么这一步的状态就是
k
<
<
j
k << j
k<<j, 否则就是
k
k
k
所以,转移就是:
- a i ⩽ 10 : a_i \leqslant 10 : ai⩽10: f [ i ] [ S + 1 < < j ] = f [ i ] [ S ] + f [ i − 1 ] [ S ] ∗ 1 a i f[i][S+1<<j] = f[i][S] + f[i-1][S] * \frac{1}{a_i} f[i][S+1<<j]=f[i][S]+f[i−1][S]∗ai1
- 否则,在 a i a_i ai 超出 10 10 10 的部分, f [ i ] [ S ] = f [ i ] [ S ] + f [ i − 1 ] [ S ] ∗ a i − 10 a i f[i][S] = f[i][S] + f[i-1][S] * \frac{a_i - 10}{a_i} f[i][S]=f[i][S]+f[i−1][S]∗aiai−10
第四步,答案怎么求
答案再简单不过了,只需要求出 ∑ S = 0 ( 1 < < 10 ) − 1 f [ n ] [ S ] ( 10 ∈ S ) \sum_{S = 0}^{(1 << 10) - 1} f[n][S](10 \in S) ∑S=0(1<<10)−1f[n][S](10∈S) 即可。
相信逆元的过程不必我多说了,不懂的请自行百度(不懂得话估计也不会做这道题…)
#include <iostream>
#include <algorithm>
using namespace std;
const int NR = 110;
const int MOD = 998244353;
long long f[NR][(1 << 11) + 2], a[NR];
long long ksm(long long x, long long y)
{
long long ans = 1;
while (y)
{
if (y & 1) ans = ans * x % MOD;
y >>= 1;
x = x * x % MOD;
}
return ans;
}
long long inv(long long x)
{
return ksm(x, MOD - 2);
}
long long max(long long x, long long y)
{
if (x > y) return x;
return y;
}
long long min(long long x, long long y)
{
if (x < y) return x;
return y;
}
signed main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
long long S = (1 << 11) - 1;
f[0][1] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= min(a[i], 10); j++)
for (int k = 0; k <= S; k++)
{
long long now = (((k << j) | k) & S);
f[i][now] = (f[i][now] + f[i-1][k] * inv(a[i]) % MOD) % MOD;
}
for (int k = 0; k <= S; k++)
f[i][k] = f[i][k] + f[i-1][k] * inv(a[i]) % MOD * max(0, a[i] - 10) % MOD;
}
long long ans = 0;
for (int i = 0; i < (1 << 10); i++)
ans = (ans + f[n][i | (1 << 10)]) % MOD;
cout << ans << '\n';
return 0;
}