题目连接 : 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 }