LG4707 重返现世(扩展 min-max 容斥)
题目链接
题意简述
有 种颜色,每秒会出现一种颜色,第 种颜色出现概率为 ,其中 ,求共出现 种颜色的期望用时,对 取模。
,,。
简要做法
扩展 min-max 容斥
其中 表示 这个集合的第 大元素, 表示 这个集合中最小的元素。
证明可以使用二项式反演,不会二项式反演也记不住式子的话,考场现推可以设 然后算。
这个式子还可以推广到期望,“第 大的期望”意思是 。( 表示事件 发生的概率。)
重返现世
下文中用 代表 。
答案就是颜色出现时间的第 大的期望。
其中 表示 , 可以这样理解:计算 中元素最早出现时间的期望,可以将 中所有颜色绑在一起,出现概率就是 ,期望出现时间就是其逆元。
其中 表示 , 可以这样理解:计算 中元素最早出现时间的期望,可以将 中所有颜色绑在一起,出现概率就是 ,期望出现时间就是其逆元。
接下来是一个神奇的 dp。
令 表示 。(这里的 与题目输入中的 不同。)( 时依然是良定义的,其值为 。)
考虑转移,分两种情况:
- 不包含 ;
- 包含 。
第一种情况显然是 。
在 时,将 这个元素从 中拿出来,剩下的部分依然不是空集,所以第二种情况的式子是 。
尝试从之前的状态转移,写出两个式子:
发现最后的组合数部分就是杨辉三角中计算组合数的方法(),而前面只是正负号的变化。
也就是说, 时,第二部分的值为 。
为什么要强调 呢?因为枚举 时是 不包含空集 的。所以,当 时,第二种情况需要特殊计算,直接将 代入定义式,得到第二部分的值为 ,也就是 时为 ,否则为 。
总结一下:
dp 的边界情况是什么?其实除了 (当然这些情况里也有很多 ),其它情况都可以由定义计算得到是 。
最后的答案就是 。
需要用滚动数组优化空间。
参考代码
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 | using namespace std; typedef long long ll; const int N = 1010; const int M = 10010; const int K = 15; const int mod = 998244353; int n, m, k, p[N], f[M][K], ans; int qpow(int x, int y) { int out = 1; while (y) { if (y & 1) out = (ll) out * x % mod; x = (ll) x * x % mod; y >>= 1; } return out; } int main() { scanf("%d%d%d", &n, &k, &m); for (int i = 1; i <= n; ++i) { scanf("%d", p + i); } k = n - k + 1; for (int i = 1; i <= n; ++i) { for (int j = k; j >= 1; --j) { for (int s = m; s > p[i]; --s) { f[s][j] = (0ll + f[s][j] + f[s - p[i]][j - 1] - f[s - p[i]][j] + mod) % mod; } } f[p[i]][1] = (f[p[i]][1] + 1) % mod; } for (int i = 1; i <= m; ++i) ans = (ans + (ll) f[i][k] * m % mod * qpow(i, mod - 2)) % mod; cout << ans; return 0; } |
如果枚举包含空集
如果我们将 dp 状态定义成枚举包含空集,也是可以算的。
令 表示 。
还是分成不包含 和包含 两部分。
第一部分依然是 。
第二部分不需要分 与 的关系讨论,直接 。
但是边界情况需要注意:
(注: 可以由广义组合数 得到。)
代码:
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 | using namespace std; typedef long long ll; const int N = 1010; const int M = 10010; const int K = 15; const int mod = 998244353; int n, m, k, p[N], f[M][K], ans; int qpow(int x, int y) { int out = 1; while (y) { if (y & 1) out = (ll) out * x % mod; x = (ll) x * x % mod; y >>= 1; } return out; } int main() { scanf("%d%d%d", &n, &k, &m); for (int i = 1; i <= n; ++i) { scanf("%d", p + i); } k = n - k + 1; for (int i = 1; i <= k; ++i) f[0][i] = mod - 1; for (int i = 1; i <= n; ++i) { for (int j = k; j >= 1; --j) { for (int s = m; s >= p[i]; --s) { f[s][j] = (0ll + f[s][j] + f[s - p[i]][j - 1] - f[s - p[i]][j] + mod) % mod; } } } for (int i = 1; i <= m; ++i) ans = (ans + (ll) f[i][k] * m % mod * qpow(i, mod - 2)) % mod; cout << ans; return 0; } |
http://ss.pythonic.life/subscribe
回复删除?
删除I like duck!
回复删除orz!
删除