问题描述
John 得到了 n 罐糖果。不同的糖果罐,糖果的种类不同(即同一个糖果罐里的糖果种类是相同的,不同的糖果罐里的糖果的种类是不同的)。第 i 个糖果罐里有 mi 个糖果。John 决定吃掉一些糖果,他想吃掉至少 a 个糖果,但不超过 b 个。问题是 John 无法确定吃多少个糖果和每种糖果各吃几个。有多少种方法可以做这件事呢?
输入格式
输入共 n+1 行:
第一行读入 n,a,b。
接下来 n 行,一行一个数,代表 mi。
输出格式
仅一行,表示 John 能够选择的满足以上条件的吃掉糖果的方法数,答案对 2004 取模。
样例输入
2 1 3
3
5
样例输出
9
数据范围
(1le nle 10,0leq a leq b leq 10^7,0 leq m_{i} leq 10^6)。
解析
比较明显的,这是一道生成函数题。
我们先构造出生成函数,为数列 {1,1,1,...1} 的生成函数。转化一下,问题就是求一下多项式的a次项系数到b次项系数的和:
这样并不好求,时间的限制不允许我们使用NTT之类的算法。我们要将之前的生成函数转化一下:
因此,我们可以得到
将分子和分母分开讨论。对于分子,同样的道理,我们知道 (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) 次项系数和。不难得到,我们有
画一画杨辉三角可以发现,(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
",(cal(b)-cal(a-1)+mod)%mod);
return 0;
}