我是靠谱客的博主 愤怒凉面,这篇文章主要介绍Codeforces 451E 容斥原理,现在分享给大家,希望可以做个参考。

题意

传送门 Codeforces 451E

题解

多重集的组合数问题。考虑每个盒子花的数量没有限制的情况,那么组合数为
C s + n − 1 n − 1 C_{s+n-1}^{n-1} Cs+n1n1 可以看做在 s + n − 1 s+n-1 s+n1 个位置中插入 n − 1 n-1 n1 个隔板,每个间隔构成了 n n n 个盒子取数的情况。需要从没有限制的取法中减去超出限制的取法的数量。设 A i A_i Ai 代表取法中从第 i i i 个盒子中取出超过限制(即大于 f i f_i fi)的花的取法,那么答案为
C s + n − 1 n − 1 − ∣ ⋃ i = 1 n A i ∣ C_{s+n-1}^{n-1}-|bigcuplimits_{i=1}^{n}A_i | Cs+n1n1i=1nAi 根据容斥原理,求解并集的时候需要计算交集。考虑 ∣ A i ∣ |A_i| Ai,预取 f i + 1 f_i+1 fi+1 朵花,其余情况任取,则仍然可以使用无限制情况下多重集组合数的求法,即 C s + n − 1 − ( f i + 1 ) n − 1 C_{s+n-1-(f_i+1)}^{n-1} Cs+n1(fi+1)n1;同理, ∣ A i ∩ A j ∣ |A_icap A_j| AiAj 取法为 C s + n − 1 − ( f i + 1 + f j + 1 ) n − 1 C_{s+n-1-(f_i+1+f_j+1)}^{n-1} Cs+n1(fi+1+fj+1)n1

设从从 n n n 个盒子中取 k k k 个盒子的集合为 S k S_k Sk,那么可以枚举集合, ∣ ∑ k = 0 n S k ∣ = 2 n |sumlimits_{k=0}^{n}S_k|=2^n k=0nSk=2n,集合 S k S_k Sk 对应项为
( − 1 ) k C s + n − 1 − ∑ i ∈ S k ( f i + 1 ) n − 1 (-1)^kC_{s+n-1-sumlimits_{iin S_k}(f_i+1)}^{n-1} (1)kCs+n1iSk(fi+1)n1 考虑到 s s s 的值很大,而 n n n 较小,将组合数写为
C n m = A n m m ! C_{n}^{m}=frac{A_{n}^{m}}{m!} Cnm=m!Anm 此时保证组合数求解复杂度为 O ( n ) O(n) O(n),使用 L u c a s Lucas Lucas 定理防止 l o n g   l o n g long long long long 溢出。总时间复杂度为 O ( n 2 n ) O(n2^n) O(n2n)

复制代码
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
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define maxn 25 const ll mod = 1000000007; ll n, s, f[maxn], inv[maxn], fv[maxn]; ll C(ll n, ll m) { if (n < m) return 0; if (m == 0) return 1; ll res = 1; for (ll i = n - m + 1; i <= n; ++i) res = (res * i) % mod; return res * fv[m] % mod; } ll Lucas(ll n, ll m) { if (n < m) return 0; if (m == 0) return 1; return C(n % mod, m % mod) * Lucas(n / mod, m / mod) % mod; } int main() { cin >> n >> s; for (int i = 0; i < n; ++i) cin >> f[i]; inv[1] = fv[0] = fv[1] = 1; for (int i = 2; i < n; ++i) { inv[i] = (mod - mod / i) * inv[mod % i] % mod; fv[i] = inv[i] * fv[i - 1] % mod; } ll res = 0; for (int i = 0; i < (1 << n); ++i) { ll sum = 0, k = 0; for (int j = 0; j < n; ++j) if (i >> j & 1) sum += f[j] + 1, ++k; res = (res + (k & 1 ? -1 : 1) * Lucas(s + n - 1 - sum, n - 1)) % mod; } cout << (res + mod) % mod << endl; return 0; }

最后

以上就是愤怒凉面最近收集整理的关于Codeforces 451E 容斥原理的全部内容,更多相关Codeforces内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(66)

评论列表共有 0 条评论

立即
投稿
返回
顶部