1 条题解
-
0
自动搬运
来自洛谷,原作者为

Sooke
做被自己雪藏的耳廓狐 | https://codeforces.com/profile/Sulfox搬运于
2025-08-24 22:02:49,当前版本为作者最后更新于2019-01-18 21:19:11,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
前言
的确是一道神仙题……网上都找不到一篇详细的题解,可能是我理解能力太差了吧,硬是瞪了好久才看懂。
为了不让大家思维受阻,这里尽我所能地解释清楚。
解题思路
首先,您需要认识下面这个式子:
$$\max_k(S) = \sum\limits_{T \subseteq S} (-1)^{|T| - k}\ C_{|T|-1}^{k-1}\min(T) $$它是 扩展 容斥 的根本。其中 表示集合 中的第 大元素, 表示集合 中的最小元素,尽管式子看起来及其玄学,但它确实是可以通过构造两个函数然后二项式反演证明的,想了解具体证明过程的读者可以上网自行学习。
有趣的是,上式可以推广到期望,即:
$$E(\max_k(S)) = \sum\limits_{T \subseteq S} (-1)^{|T| - k}\ C_{|T|-1}^{k-1}E(\min(T)) $$但在这题中,所谓的 和 都是什么玩意儿?连集合都没有,而且哪来的相对大小?
我们可以假象每一种原料都有一个出现的时间,因为它们出现的时间互不相同,所以可以构成一个集合,所谓 显然就是 包含的原料最早出现的期望时间, 同理。至于相对大小,因为我们求的本来就是期望,相对对我们来说不重要,只要严格遵循上式计算即可。
为什么要用到这个式子呢?原因是直接算 太难了,而 ,我们把 中、 外的原料分别看成整体,每次刷到 中原料的概率是 ,期望时间自然就是 。
另外还有一点,题目里给出的 ,实际上代表 ,以下令 ,以简化得到纯正的 。
好了,该扯的都扯完了,下面我们进入本题最核心的环节——设计 。
考虑到 和转化后的 特别小,很快就能~~(猜)~~想出分别给它们一维。完整地, 表示确定式子中 的值,当前是第 种原料, 时的 $\sum\limits_{T \subseteq S} (-1)^{|T| - k}\ C_{|T|-1}^{k-1}$ 的值。
状态很复杂对吧,其实本质上就是把 和 表示到状态里,给剩下的东西求和,最后再统计状态的贡献。
接下来是转移,很像背包,对于第 种原料,如果不选,,因为没有影响,如果选呢?就有点复杂了。
对于 这两维,必定分别由 转移而来,但现在主要问题是,如果直接转移,转移后的所有 都比转移前大 (塞了个 进去),怎么处理其影响?
幸运的是, 大 ,式子里的 仅仅改了个正负性,而仔细观察 ,如果 大 ,它可以拆成 (组合数的递推式)!
这就好办多了,拆成两个状态, 的 不变,由于 变了 ,所以 对 有 的贡献。 的 小 , 也变了 ,负负得正, 对 有 的贡献。综上所述,如果选,$f_{k,\,i,\,j} = f_{k-1,\,i-1,\,j-p_i} - f_{k,\,i-1,\,j-p_i}$。真是神仙!
状态、转移、边界三步走,此时还剩最后一个难关,边界!
如何初始化 ,使得整个 滴水不漏呢?全部设 显然是不行的,毕竟加加减减也弄不出其他数来,这里有个巧妙的方法,令 ,其他 。奇怪的设定,看起来违背状态的定义,实际上是从其他状态反推而来的唯一设定,其可以保证 时的贡献恰好等于 , 时的贡献恰好等于 ,详见转移式,大家不妨自己去推推看。
最后,枚举 中的 ,用逆元计算取模意义下的 ,就可以得出该状态的总贡献啦!
代码实现
时空复杂度都是 ,直接开 数组是绝对开不下的,由于其转移过程类似背包,可以把 这一维压掉。
乍一看代码好短,然而包含的思维量却是遥不可及的。
#include <cstdio> #include <algorithm> inline int read() { char c = getchar(); int x = 0; while (c < '0' || c > '9') { c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + (c & 15); c = getchar(); } return x; } const int maxN = 1005, maxM = 10005, p = 998244353; inline int add(int x, int y) { x += y; return x >= p ? x - p : x; } inline int sub(int x, int y) { x -= y; return x < 0 ? x + p : x; } int n, m, s, w, ans, inv[maxM], f[12][maxM]; int main() { n = read(); s = n + 1 - read(); m = read(); for (int i = (inv[1] = 1) + 1; i <= m; i++) { inv[i] = (long long) inv[p % i] * (p - p / i) % p; } for (int i = 1; i <= s; i++) { f[i][0] = -1; } for (int i = 1; i <= n; i++) { w = read(); for (int j = m; j >= w; j--) { for (int k = s; k; k--) { f[k][j] = add(f[k][j], sub(f[k - 1][j - w], f[k][j - w])); } } } for (int i = 1; i <= m; i++) { ans = (ans + (long long) f[s][i] * inv[i] % p) % p; } printf("%lld\n", (long long) ans * m % p); return 0; }
- 1
信息
- ID
- 3615
- 时间
- 2000ms
- 内存
- 125MiB
- 难度
- 7
- 标签
- (无)
- 递交数
- 0
- 已通过
- 0
- 上传者