欢迎来到下蛋爷之家|

下蛋爷

园龄:4年2个月粉丝:8关注:23

P4707 重返现世 题解

Description

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

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

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

1n10001kn,|nk|100pim,p=m,1m10000

Solution

先让 knk+1,那么原题相当于求 E(kMax(T)),这玩意可以用 min-max 容斥转化为:

|S|k(1)|S|kE(Min(S))(|S|1k1)

容易发现对于一个集合 S,它的第一次出现的期望就是 miSpi,然后对这个进行 dp 即可。

fi,j,k 表示考虑到了编号 1i,选了 j 个数,目前和为 k 的方案数,直接进行 dp 是 O(n2m) 的,过不了。

考虑优化。

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

gi,j 表示目前考虑了 1i,和为 j 的所有方案的 (1)|S|k(|S|1k)pi

那么如果 i 不选,则 gi,jgi1,j

如果 i 选,会发现哪个组合数不好搞,但是注意到 (|S|1k1)=(|S|2k1)+(|S|2k2),且前面的 1 是好处理的,所以那个组合数也可以 dp 求出。

但是这里 k 会变化且很小,所以可以记一维 k,即 gi,j,k=gi1,j,k+gi1,jpi,k1gi1,jpi,k

边界是 g0,0,0=1

时间复杂度:O(nm(nk))

Code

#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;
}
posted @   下蛋爷  阅读(4)  评论(0编辑  收藏  举报
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示