Devu想用花去装饰他的花园,他已经购买了n个箱子,第i个箱子有fi朵花,在同一个的箱子里的所有花是同种颜色的(所以它们没有任何其他特征)。另外,不存在两个箱子中的花是相同颜色的。 现在Devu想从这些箱子里选择s朵花去装饰他的花园,Devu想要知道,总共有多少种方式从这些箱子里取出这么多的花?因为结果有可能会很大,结果需要对1000000007取模。 Devu认为至少有一个箱子中选择的花的数量不同才是两种不同的方案。
首先这是一道多重集的题目。
形如 S = { n 1 ∗ a 1 , n 2 ∗ a 2 … … , n k ∗ a k } S =\{n_1*a_1,n_2*a_2……,n_k*a_k\} S={n1∗a1,n2∗a2……,nk∗ak}的集合被称为多重集
首先我们需要解决在这个集合中选出r个数的问题。
那么这是所有组合的方案,对于这道题我们需要将不合法的方案去掉。
对于第i个物品而言它的个数是受限制的,想让组合不合法,当且仅当第i个数选出了 ≥ n i + 1 ≥ n_i+1 ≥ni+1个物品。
第一个物品不合法的方案:
这其实就是一个容斥原理,将集合大小为1的集合减去再讲集合大小为2的集合加上在减去集合大小为3的集合……以此类推。
然后由于这道题n的范围如此之小,这就给了我们状压的冲动。
将集合看为二进制,1作为选当前盒子,0视为不选。
需要注意的是,计算组合是,最好变形成排列在算组合,由于模数是质数,所以预处理出逆元即可。
#include
using namespace std;
#define int long long
const int p = 1e9+7;
int n,m;
int Ans;
int inv[1010100];
int f[1010100];
int power(int x,int y){
int ans = 1;
while (y){
if (y & 1) ans = (ans*x)%p;
y>>=1;
x = (x*x)%p;
// cout<<1<
}
return ans;
}
int C(int x,int y){
if (x<0 || y<0 || x<y) return 0;
x%=p;
if (!y || !x) return 1;
int anss = 1;
for (int i=0;i<y;i++)
anss = (anss*(x-i))%p;
for (int i=1;i<=y;i++)
anss = (anss*inv[i])%p;
return anss;
}
signed main(){
scanf("%lld %lld",&n,&m);
for (int i=1;i<=n;i++) scanf("%lld",&f[i]);
for (int i=1;i<=21;i++)
inv[i] = power(i,p-2);//预处理逆元
Ans = C(n+m-1,n-1);//组合总数
for (int i=1;i<1<<n;i++){
int t = n+m,P=0;
for (int j=0;j<n;j++)
if (i&(1<<j)) t-=f[j+1],P++;//枚举每一位,看一下1的位数,处理处上面的Σ的东西
t-=P+1;
if (P%2) Ans = (Ans-C(t,n-1)+p)%p;
else Ans = (Ans+C(t,n-1))%p;//容斥原理
}
printf("%lld",(Ans+p)%p);
}