BZOJ3027: [Ceoi2004]Sweet【组合】

题目描述:

John得到了n罐糖果。不同的糖果罐,糖果的种类不同(即同一个糖果罐里的糖果种类是相同的,不同的糖果罐里的糖果的种类是不同的)。第i个糖果罐里有 mi个糖果。John决定吃掉一些糖果,他想吃掉至少a个糖果,但不超过b个。问题是John 无法确定吃多少个糖果和每种糖果各吃几个。有多少种方法可以做这件事呢?

题目分析:

求吃 k k k个糖果的方案数即下面这个方程的整数解个数:
x 1 + x 2 + . . . + x n = k ( 0 ≤ x i ≤ m i ) x_1+x_2+...+x_n=k\\(0\le x_i\le m_i) x1+x2+...+xn=k(0ximi)
用隔板法及容斥原理可知解的个数为 ∑ S ∈ { 1 , 2.. n } ( − 1 ) ∣ S ∣ C k − ( ∑ i ∈ S m i + 1 ) + n − 1 n − 1 \sum_{S\in\{1,2..n\}}(-1)^{|S|}C_{k-(\sum_{i\in S}m_i+1)+n-1}^{n-1} S{1,2..n}(1)SCk(iSmi+1)+n1n1

现在 k k k可以取 [ a , b ] [a,b] [a,b],所以相当于要求 ∑ S ∈ { 1 , 2.. n } ( − 1 ) ∣ S ∣ ∑ k = 0 N C k − ( ∑ i ∈ S m i + 1 ) + n − 1 n − 1 \sum_{S\in\{1,2..n\}}(-1)^{|S|}\sum_{k=0}^NC_{k-(\sum_{i\in S}m_i+1)+n-1}^{n-1} S{1,2..n}(1)Sk=0NCk(iSmi+1)+n1n1

k &lt; ∑ i ∈ S m i + 1 k&lt;\sum_{i\in S}m_i+1 k<iSmi+1时,C=0;当 k ≥ ∑ i ∈ S m i + 1 k\ge \sum_{i\in S}m_i+1 kiSmi+1时,后面的C的和即为 ∑ p = 0 N − ∑ i ∈ S m i + 1 C p + n − 1 n − 1 = C n − 1 n − 1 + C n n − 1 + C n + 1 n − 1 . . . \sum_{p=0}^{N-\sum_{i\in S}m_i+1}C_{p+n-1}^{n-1}=C_{n-1}^{n-1}+C_n^{n-1}+C_{n+1}^{n-1}... p=0NiSmi+1Cp+n1n1=Cn1n1+Cnn1+Cn+1n1...

因为 C n − 1 n − 1 = C n n = 1 C_{n-1}^{n-1}=C_n^n=1 Cn1n1=Cnn=1,且 C i j = C i − 1 j − 1 + C i − 1 j C_i^j=C_{i-1}^{j-1}+C_{i-1}^j Cij=Ci1j1+Ci1j,所以上式 = C n n + C n n − 1 + C n + 1 n − 1 . . . = C n + 1 n + C n + 1 n − 1 + C n + 2 n − 1 . . . = C n + 2 n + C n + 2 n − 1 + . . . = C N − ∑ i ∈ S m i + 1 + n n =C_n^n+C_{n}^{n-1}+C_{n+1}^{n-1}...=C_{n+1}^n+C_{n+1}^{n-1}+C_{n+2}^{n-1}...=C_{n+2}^n+C_{n+2}^{n-1}+...=C_{N-\sum_{i\in S}m_i+1+n}^n =Cnn+Cnn1+Cn+1n1...=Cn+1n+Cn+1n1+Cn+2n1...=Cn+2n+Cn+2n1+...=CNiSmi+1+nn

对于模数为2004的情况,这位仁兄VisJiao的博客说的很好:
在这里插入图片描述

另外,上面的推导过程可以用生成函数,详见这篇blog

Code:

#include<cstdio>
#include<algorithm>
#define maxn 15
using namespace std;
const int mod = 2004;
int n,L,R,a[maxn],fac,ans;
int C(int n,int m){//m==n
	if(n<m) return 0;
	static const long long Mod = 1ll*fac*mod;//long long attention!
	long long ret=1;
	for(int i=n-m+1;i<=n;i++) ret=ret*i%Mod;
	return ret/fac;
}
int main()
{
	scanf("%d%d%d",&n,&L,&R);
	fac=1; for(int i=2;i<=n;i++) fac*=i;
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	for(int s=0;s<1<<n;s++){
		int typ=1,tmp=0;
		for(int i=0;i<n;i++) if(s>>i&1) typ=-typ,tmp+=a[i]+1;
		ans=(ans+typ*(C(R-tmp+n,n)-C(L-1-tmp+n,n)))%mod;
	}
	printf("%d\n",(ans+mod)%mod);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值