P4707 重返现世 题解

Description

为了打开返回现世的大门,Yopilla 需要制作开启大门的钥匙。Yopilla 所在的迷失大陆有 nn 种原料,只需要集齐任意 kk 种,就可以开始制作。

Yopilla 来到了迷失大陆的核心地域。每个单位时间,这片地域就会随机生成一种原料。每种原料被生成的概率是不同的,第 ii 种原料被生成的概率是 pim\frac{p_i}{m}。如果 Yopilla 没有这种原料,那么就可以进行收集。

Yopilla 急于知道,他收集到任意 kk 种原料的期望时间,答案对 998244353998244353 取模。

1n10001 \le n \le 10001kn,nk101 \le k \le n, \lvert n - k \rvert \le 100pim,p=m,1m100000 \le p_i \le m, \sum p = m, 1 \le m \le 10000

Solution

先让 knk+1k\leftarrow n-k+1,那么原题相当于求 E(kMax(T))E\left(\text{kMax}(T)\right),这玩意可以用 min-max 容斥转化为:

Sk(1)SkE(Min(S))(S1k1)\sum_{|S|\geq k}{(-1)^{|S|-k}E\left(\text{Min}(S)\right)\binom{|S|-1}{k-1}}

容易发现对于一个集合 SS,它的第一次出现的期望就是 miSpi\dfrac{m}{\sum_{i\in S}{p_i}},然后对这个进行 dp 即可。

fi,j,kf_{i,j,k} 表示考虑到了编号 1i1\sim i,选了 jj 个数,目前和为 kk 的方案数,直接进行 dp 是 O(n2m)O(n^2m) 的,过不了。

考虑优化。

注意到 pi\sum{p_i} 这个东西在分母,所以一定是无法优化掉的,于是可以考虑把个数那一维去掉。

gi,jg_{i,j} 表示目前考虑了 1i1\sim i,和为 jj 的所有方案的 (1)Sk(S1k)pi\sum{\frac{(-1)^{|S|-k}\binom{|S|-1}{k}}{\sum_{p_i}}}

那么如果 ii 不选,则 gi,jgi1,jg_{i,j}\leftarrow g_{i-1,j}

如果 ii 选,会发现哪个组合数不好搞,但是注意到 (S1k1)=(S2k1)+(S2k2)\binom{|S|-1}{k-1}=\binom{|S|-2}{k-1}+\binom{|S|-2}{k-2},且前面的 1-1 是好处理的,所以那个组合数也可以 dp 求出。

但是这里 kk 会变化且很小,所以可以记一维 kk,即 gi,j,k=gi1,j,k+gi1,jpi,k1gi1,jpi,kg_{i,j,k}=g_{i-1,j,k}+g_{i-1,j-p_i,k-1}-g_{i-1,j-p_i,k}

边界是 g0,0,0=1g_{0,0,0}=1

时间复杂度:O(nm(nk))O\left(nm(n-k)\right)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e3 + 5, kMaxM = 1e4 + 5, kMaxK = 15, kMod = 998244353;

int n, k, m;
int p[kMaxN], f[2][kMaxM][kMaxK];

constexpr int qpow(int bs, int64_t idx = kMod - 2) {
int ret = 1;
for (; idx; idx >>= 1, bs = (int64_t)bs * bs % kMod)
if (idx & 1)
ret = (int64_t)ret * bs % kMod;
return ret;
}

inline int add(int x, int y) { return (x + y >= kMod ? x + y - kMod : x + y); }
inline int sub(int x, int y) { return (x >= y ? x - y : x - y + kMod); }
inline void inc(int &x, int y) { (x += y) >= kMod ? x -= kMod : x; }
inline void dec(int &x, int y) { (x -= y) < 0 ? x += kMod : x; }

void dickdreamer() {
std::cin >> n >> k >> m;
k = n - k + 1;
for (int i = 1; i <= n; ++i) std::cin >> p[i];
int o = 0;
f[o][0][0] = 1;
for (int i = 1; i <= n; ++i) {
if (!p[i]) continue;
o ^= 1;
for (int j = 0; j <= m; ++j)
for (int s = 0; s <= k; ++s)
f[o][j][s] = f[o ^ 1][j][s];
for (int j = p[i]; j <= m; ++j) {
for (int s = 1; s <= k; ++s) {
inc(f[o][j][s], sub(f[o ^ 1][j - p[i]][s - 1], f[o ^ 1][j - p[i]][s]));
}
}
}
int ans = 0;
for (int i = 1; i <= m; ++i) {
inc(ans, 1ll * f[o][i][k] * m % kMod * qpow(i) % kMod);
}
std::cout << ans << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P4707 重返现世 题解
https://sobaliuziao.github.io/2024/04/26/post/49bc8af4.html
作者
Egg_laying_master
发布于
2024年4月26日
许可协议