bzoj3027: [Ceoi2004]Sweet(生成函数+搜索)

传送门
生成函数好题。
题意简述:给出n个盒子,第 i i i个盒子里有 m i m_i mi颗相同的糖(但不同盒子中的糖不相同),问有多少种选法可以从各盒子中选出数量在 [ a , b ] [a,b] [a,b]之间的糖果。


思路:先对每个盒子构造出生成函数: 1 + x 2 + . . . + x m i = 1 − x m i + 1 1 − x 1+x^2+...+x^{m_i}=\frac{1-x^{m_i+1}}{1-x} 1+x2+...+xmi=1x1xmi+1

然后把所有盒子的生成函数乘起来: F ( x ) = ∏ i = 1 n ( 1 − x m i + 1 ) ( 1 − x ) n = ( 1 + x + x 2 + . . . ) n ∏ i = 1 n ( 1 − x m i + 1 ) F(x)=\frac{\prod_{i=1}^n(1-x^{m_i+1})}{(1-x)^n}=(1+x+x^2+...)^n\prod_{i=1}^n(1-x^{m_i+1}) F(x)=(1x)ni=1n(1xmi+1)=(1+x+x2+...)ni=1n(1xmi+1)
这个时候考虑如何统计答案。
直接做很难,因此我们差分一下,转化成求 f ( b ) − f ( a − 1 ) f(b)-f(a-1) f(b)f(a1) f ( x ) f(x) f(x)表示选出数量不超过 x x x的糖果的方案数。

左边的一坨 x m x^m xm的系数看成把 m m m拆成 n n n个自然数,为 C m + n − 1 n − 1 C_{m+n-1}^{n-1} Cm+n1n1
右边的一坨爆搜即可。
然后对于右边搜出来的 k x t kx^t kxt,假设当前要求数量不超过 m m m,那么这一种组合方式对答案的贡献就是: k ∗ ( C n − 1 n − 1 + C n n − 1 + . . . + C n + m − 1 − t n − 1 ) = k C n + m − t n k*(C_{n-1}^{n-1}+C_{n}^{n-1}+...+C_{n+m-1-t}^{n-1})=kC_{n+m-t}^{n} k(Cn1n1+Cnn1+...+Cn+m1tn1)=kCn+mtn
这样就可以更新答案了。
注意模数的处理
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int mod=2004;
int m[12],N,a,b,fac=1,sum=0;
inline int C(int n,int m){
	if(n<m)return 0;
	ll Mod=(ll)mod*fac,ret=1;
	for(ri i=n-m+1;i<=n;++i)ret=(ll)i%Mod*ret%Mod;
	return (ret/fac)%mod;
}
inline void dfs(int dep,int type,int idx,int lim){
	if(dep==N+1){(sum+=type*C(lim+N-idx,N)%mod)%=mod;return;}
	dfs(dep+1,type,idx,lim),dfs(dep+1,-type,idx+m[dep]+1,lim);
}
inline int calc(int lim){return sum=0,dfs(1,1,0,lim),sum;}
int main(){
	scanf("%d%d%d",&N,&a,&b);
	for(ri i=1;i<=N;++i)scanf("%d",&m[i]),fac*=i;
	cout<<((calc(b)-calc(a-1))%mod+mod)%mod;
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值