题解
有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\n", solve());
return 0;
}