题解
有n个花坛,要选s支花,每个花坛有f[i]支花,同一个花坛的花颜色相同,不同花坛的花颜色不同,问说可以有多少种组合。
2^n的状态,枚举某些花坛的花超过了,剩下的用隔板法计算个数,再加个容斥原理就行了
————————————————————————————————————————————-
看来我应该写详细点
首先隔板法sum个球放进n个盒子中允许盒子为空的方案是C(sum+n-1,n-1)
但是由于这里有个限制,即f[i],这样计数的话某些花会超出其个数
所以我们应该2^n枚举某些花超出了个数,比如确定i超出个数其它不确定的方案C(sum-f[i]-1+n-1,n-1)
即sum-=f[i]+1(因为允许箱子为空一开始要人为加入一个球)
这里再加个容斥原理+超0个的-超1个的+超2个的……
还有要用到乘法逆元求C和lucas定理
根据费马小定理a^(p-1)=1(mod p),a为质数
a^(p-2)=a^(-1)(mod p),那么a^(p-2)就是a在modp意义下的逆元
所以可以将C的公式上下分别得出取模后的乘积s1,s2,将s1乘s2的逆元
lucas定理:C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)
前半部分继续递归用lucas定理,后半部分直接求
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1e9+7; ll pow_mod(ll a,ll b) { ll res = 1; while (b) { if (b & 1) res = res * a % mod; a = a * a % mod; b >>= 1; } return res; } ll C(int n,int m) { if (n < m) return 0; if (m > n - m) m = n - m; ll s1 = 1, s2 = 1; for (ll i = 0; i < m; i++) { s1 = s1 * (n - i) % mod; s2 = s2 * (i + 1) % mod; } return s1 * pow_mod(s2, mod - 2) % mod; } ll lucas(ll n,ll m) { if (m == 0) return 1; return C(n % mod, m % mod) * lucas(n / mod, m / mod) % mod; } int n; ll s,f[25]; ll solve() { ll ans = 0; for (int i = 0; i < 1 << n; i++) { ll sign = 1, sum = s; for (int j = 0; j < n; j++) { if (i >> j & 1) { sum -= f[j]+1; sign *= -1; } } if (sum < 0) continue; ans += sign * lucas(sum + n - 1, n - 1); ans %= mod; } return (ans + mod) % mod; } int main() { scanf("%d%lld", &n, &s); for (int i = 0; i < n; i++) { scanf("%lld", &f[i]); } printf("%lld ", solve()); return 0; }