【组合数学 && 容斥原理】 Devu and Flowers

题目传送门

题目描述:

Devu想用花去装饰他的花园,他已经购买了n个箱子,第i个箱子有fi朵花,在同一个的箱子里的所有花是同种颜色的(所以它们没有任何其他特征)。另外,不存在两个箱子中的花是相同颜色的。 现在Devu想从这些箱子里选择s朵花去装饰他的花园,Devu想要知道,总共有多少种方式从这些箱子里取出这么多的花?因为结果有可能会很大,结果需要对1000000007取模。 Devu认为至少有一个箱子中选择的花的数量不同才是两种不同的方案。


Solution

首先这是一道多重集的题目。

形如 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={n1a1,n2a2,nkak}的集合被称为多重集
首先我们需要解决在这个集合中选出r个数的问题。

  • 这个问题利用插板法可以很容易的解决,答案即为: C k − r + 1 k − 1 C_{k-r+1}^{k-1} Ckr+1k1

那么这是所有组合的方案,对于这道题我们需要将不合法的方案去掉。
对于第i个物品而言它的个数是受限制的,想让组合不合法,当且仅当第i个数选出了 ≥ n i + 1 ≥ n_i+1 ni+1个物品。

第一个物品不合法的方案:

  • 利用插板法可以得到不合法的方案为 C k − r − n 1 − 2 C_{k-r-n_1-2} Ckrn12

这其实就是一个容斥原理,将集合大小为1的集合减去再讲集合大小为2的集合加上在减去集合大小为3的集合……以此类推。

这样就可以得到以下公式:
在这里插入图片描述

然后由于这道题n的范围如此之小,这就给了我们状压的冲动。
将集合看为二进制,1作为选当前盒子,0视为不选。

需要注意的是,计算组合是,最好变形成排列在算组合,由于模数是质数,所以预处理出逆元即可。


Code

#include<bits/stdc++.h>
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<<endl;
	}
	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);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值