CF 451E Devu and Flowers(容斥原理+组合数学)

题目连接 : http://codeforces.com/contest/451/problem/E

题意 : 从n个箱子里面选择s朵花,问有多少种不同的选择。

《组合数学》上有类似的题目,大概在6.2章。(写一下主要是让自己记的深些- -b)

首先你得会算这样一个问题,就是把m个等价的物品放到n个不同的容器中的方法数为C(n+m-1, m-1), 这个用挡板法很容易证明的。

然后假如每个箱子是无穷的多的话(或者>=s),答案就是C(n+s-1, n-1);

然后算S{i}:就是限定第i个至少要拿f[i]+1朵花的前提下的方案数。

S{i, j}就是表示第i个和第j个分别要拿f[i]+1和f[j]+1朵花的方案数。

。。。

这个样的话最后的ans = C(n+s-1, n-1) - S{1}- S{2}...S{i},.. + S{1, 2} + S{i, j} ... - S{i, j, k}... // 这里就是用到容斥原理了。。

这道题目n比较小,所以就直接枚举好了,复杂度为O(n * 2^n),小心他有的数据时10^14在乘法的时候不要爆了。

代码 :

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <iostream>
 5 
 6 using namespace std;
 7 typedef __int64 lld;
 8 const int MOD = 1e9 + 7;
 9 
10 lld exgcd(lld a, lld b, lld &x, lld &y) {
11     if (!b) {
12         x = 1; y = 0;
13         return a;
14     }
15     lld ret = exgcd(b, a%b, y, x);
16     y -= (a/b) * x;
17     return ret;
18 }
19 
20 lld Inv(lld a, lld MOD) {
21     lld x, y;
22     lld d = exgcd(a, MOD, x, y);
23     return d == 1 ? (x%MOD+MOD)%MOD : printf("no\n");
24 }
25 lld calc(lld N, int M) {
26     lld ret = 1;
27     for (int i = 0; i < M; i++) {
28         ret = ret * ((N-i)%MOD) % MOD * Inv(i+1, MOD) % MOD;
29     }
30     return ret;
31 }
32 
33 lld s;
34 int n;
35 lld f[105];
36 int main() {
37     cin >> n >> s;
38     for (int i = 0; i < n; i++) {
39         cin >> f[i];
40     }
41     lld ret = 0;
42     for (int i = 0; i < (1<<n); i++) {
43         lld rem = s;
44         int fg = 1;
45         for (int j = 0; j < n; j++) {
46             if (i >> j & 1) {
47                 fg = -fg;
48                 rem -= (f[j] + 1);
49             }
50         }
51         if (rem < 0) continue;
52         ret += fg * calc(rem + n - 1, n - 1);
53         ret %= MOD;
54     }
55     cout << (ret%MOD + MOD) % MOD << endl;
56     return 0;
57 }
View Code
原文地址:https://www.cnblogs.com/danceonly/p/3868760.html