[bzoj3027][Ceoi2004]Sweet【生成函数】【组合数】

[题目描述]

3027: [Ceoi2004]Sweet

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 137   Solved: 68
[ Submit][ Status][ Discuss]

Description

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

Input


从标准输入读入每罐糖果的数量,整数a到b 
 
John能够选择的吃掉糖果的方法数(满足以上条件)  
 

Output


 
把结果输出到标准输出(把答案模 2004 输出) 

1<=N<=10,0<=a<=b<=10^7,0<=Mi<=10^6

Sample Input

2 1 3
3
5

Sample Output

9

HINT

(1,0),(2,0),(3,0),(0,1),(0,2),(0,3),(1,1),(1,2),(2,1) 

Source

[题解]
用生成函数将答案表示出来,函数为:
1/((1-x)^n)*(1-x^(m1+1))*(1-x^(m2+1))*...*(1-x^(mn+1))
第2项到第(m+1)项乘出的非零项不会超过2^n,暴力枚举计算贡献;
第一项为一串组合数的前缀和,设当前枚举的后面的项的次数为k
ans=C(n-1,n-1)+C(n,n-1)+..+C(n+lim-k-1,n-1)
=C(n,n)+C(n,n-1)+..+C(n+lim+k-1,n-1) 
因为C(n,n)+C(n,n-1)=C(n+1,n),所以ans=C(n+lim+k,n);
在取模时C(n,m)=n!/(m!*(n-m)!)%P,可以先将P*m!最后将答案除m!
证明:设C(n,m)%(P*m!)=A 即A+k*Pm!=n!/(m-n)!
A/m!+k*P=n!/(m-n)!m! 因为k*P∈Z,C(n,m)∈Z 所以A/m!∈Z
因此 C(n,m)%P=A/m!
[代码]
/* --------------
    user Vanisher
    problem bzoj-3027
----------------*/
# include 
# define 	ll 		long long
# define 	N 		11
# define 	M 		10000100
# define 	P 		2004
using namespace std;
int m[N],a,b,ans,n;
int read(){
	int tmp=0, fh=1; char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
	while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
	return tmp*fh;
}
int C(int n, int m){
    if (n C(n+lim-k-1,n);
	ans=(ans+C(n+lim-k,n)*tag+P)%P;
}
void dfs(int k, int now, int lim, int tag){
	if (now>lim) return;
	if (k>n) {
		check(now,lim,tag);
		return;
	}
	dfs(k+1,now,lim,tag);
	dfs(k+1,now+m[k]+1,lim,tag*(-1));
}
int main(){
	n=read(), a=read(), b=read();
	for (int i=1; i<=n; i++) m[i]=read();
	dfs(1,0,a-1,-1); dfs(1,0,b,1);
	cout<


你可能感兴趣的:(【生成函数】,【组合数】)