[洛谷P6078] CEOI2004 candy

问题描述

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

输入格式

输入共 n+1 行:

第一行读入 n,a,b。

接下来 n 行,一行一个数,代表 mi。

输出格式

仅一行,表示 John 能够选择的满足以上条件的吃掉糖果的方法数,答案对 2004 取模。

样例输入

2 1 3
3
5

样例输出

9

数据范围

\(1\le n\le 10,0\leq a \leq b \leq 10^7,0 \leq m_{i} \leq 10^6\)

解析

比较明显的,这是一道生成函数题。

我们先构造出生成函数,为数列 {1,1,1,...1} 的生成函数。转化一下,问题就是求一下多项式的a次项系数到b次项系数的和:

\[\prod_{i=1}^{n}(1+x+x^2+...+x^{m_i}) \]

这样并不好求,时间的限制不允许我们使用NTT之类的算法。我们要将之前的生成函数转化一下:

\[\begin{align} F(x)&=\sum_{i=0}^mf_ix^i\\ &=\sum_{i=0}^m(f_{i-1}+[n=0])x^i\\ &=(\sum_{i=0}^m f_{i-1}x^i)+1\\ &=x(F(x)-x^m)+1 \end{align} \]

因此,我们可以得到

\[F(x)=\frac{1-x^{m_i+1}}{1-x} \]

将分子和分母分开讨论。对于分子,同样的道理,我们知道 \(\frac{1}{1-x}=1+x+x^2+...\),那么 \((\frac{1}{x-1})^n\) 对多项式 \(i\) 次项的系数的贡献为 \(C_{i+n-1}^{n-1}\)(由插板法可以比较简单地得到这个结论)。对于分母,由于n很小,我们可以直接搜索得到每一次项的系数,记为 \(a_i\)

回到原问题上面来。设\(S(i)\)表示前 \(i\) 次项系数和。不难得到,我们有

\[S(x)=\sum_{i=0}^{x}a_i\sum_{j=0}^{x-i}C_{j+n-1}^{n-1} \]

画一画杨辉三角可以发现,\(\sum_{j=0}^{x-i}C_{j+n-1}^{n-1}\) 是三角上连续一列的和,即 \(C_{n+x-j}^x\) 。然后就可以一边搜索分子的各项系数一边统计答案了。

代码

#include <iostream>
#include <cstdio>
#define int long long
#define N 12
using namespace std;
const int mod=2004;
int n,a,b,i,m[N],fac,ans;
int read()
{
	char c=getchar();
	int w=0;
	while(c<'0'||c>'9') c=getchar();
	while(c<='9'&&c>='0'){
		w=w*10+c-'0';
		c=getchar();
	}
	return w;
}
int C(int n,int m)
{
	if(n<m) return 0;
	int ans=1;
	for(int i=n-m+1;i<=n;i++) ans=ans*i%(fac*mod);
	return (ans/fac)%mod;
}
void dfs(int x,int a,int b,int c)
{
	if(x==n+1){
		int tmp=C(n+c-a,n);
		if(b<0) ans=(ans-tmp+mod)%mod;
		else if(b>0) ans=(ans+tmp)%mod;
		return;
	}
	dfs(x+1,a,b,c);
	dfs(x+1,a+m[x]+1,-b,c);
}
int cal(int a)
{
	ans=0;
	dfs(1,0,1,a);
	return ans;
}
signed main()
{
	n=read();a=read();b=read();
	for(i=1;i<=n;i++) m[i]=read();
	for(i=fac=1;i<=n;i++) fac*=i;
	printf("%lld\n",(cal(b)-cal(a-1)+mod)%mod);
	return 0;
}
posted @ 2020-07-26 08:06  CJlzf  阅读(155)  评论(1编辑  收藏  举报