题目大意
求有多少个子集族,满足:
- 其中的每个子集都是 $[n]$ 的一个子集;
- 任意两个子集互不相同;
- $1, 2, \cdots, n$ 都在其中出现了至少两次。
答案对素数 $m$ 取模。
数据范围:$n \le 3000, 10^8 \le m \le 10^9 + 9$。
思路分析
我们考虑容斥,枚举出现了不超过一次的数的个数 $i$,这样答案就是 $\sum_{i = 0}^{n} (-1)^i \binom{n}{i} A_i$。
对于某个 $i$,我们不妨假定出现次数不超过一次的数是 $1, 2, \cdots, i$。考虑继续枚举包含 $[1, i]$ 中数的子集个数 $j$。对于除了这 $j$ 个集合以外的集合,它们都不包含前 $i$ 个数,所以方案数是 $2^{2^{n - i}}$。而对于这 $j$ 个集合,发现它们必定两两不同。它们的后 $n - i$ 位共有 $(2^{n - i})^j$ 种可能的取值,而前 $i$ 位每一位都出现了不超过 $1$ 次。这相当于从前 $i$ 个数中选出 $k$ 个数钦定它们没有出现,然后把剩下 $i - k$ 个数分给 $j$ 个集合(第二类斯特林数)。这样,我们已经可以得到答案的式子:
直接计算的时间复杂度是 $O(n^3)$,不能通过。我们考虑把枚举 $k$ 变为直接计算某个式子。发现下面的等式始终成立:
考虑该等式的组合意义:左边是将 $n$ 个不同的小球选一些出来,然后划成 $k$ 个集合;右边是将 $n + 1$ 个小球划成 $k + 1$ 个集合。我们把第 $n + 1$ 个小球看作特殊小球,它被划分到的集合中的所有球都会被扔掉,这样两边的组合意义就相同了,我们就证明了这个等式。
于是原式可以简化为:
枚举 $i, j$ 直接计算即可,时间复杂度 $O(n^2)$。
代码实现
1 |
|
2 | using namespace std; |
3 | |
4 | const int maxn = 3e3; |
5 | int n, mod, C[maxn + 3][maxn + 3], S[maxn + 3][maxn + 3]; |
6 | |
7 | inline int func(int x) { |
8 | return x < mod ? x : x - mod; |
9 | } |
10 | |
11 | int qpow(int a, int b, int m = mod) { |
12 | int c = 1; |
13 | for (; b; b >>= 1, a = 1ll * a * a % m) { |
14 | if (b & 1) c = 1ll * a * c % m; |
15 | } |
16 | return c; |
17 | } |
18 | |
19 | void prework(int n) { |
20 | C[0][0] = S[0][0] = 1; |
21 | for (int i = 1; i <= n; i++) { |
22 | C[i][0] = 1; |
23 | for (int j = 1; j <= i; j++) { |
24 | C[i][j] = func(C[i - 1][j - 1] + C[i - 1][j]); |
25 | S[i][j] = (1ll * S[i - 1][j] * j + S[i - 1][j - 1]) % mod; |
26 | } |
27 | } |
28 | } |
29 | |
30 | int main() { |
31 | scanf("%d %d", &n, &mod); |
32 | prework(n + 1); |
33 | int ans = 0; |
34 | for (int i = 0; i <= n; i++) { |
35 | int x = i & 1 ? mod - C[n][i] : C[n][i], s = 0; |
36 | x = 1ll * x * qpow(2, qpow(2, n - i, mod - 1)) % mod; |
37 | for (int j = 0; j <= i; j++) { |
38 | int y = qpow(2, (n - i) * j); |
39 | y = 1ll * y * S[i + 1][j + 1] % mod; |
40 | s = func(s + y); |
41 | } |
42 | ans = (ans + 1ll * x * s) % mod; |
43 | } |
44 | printf("%d\n", ans); |
45 | return 0; |
46 | } |