1. 程式人生 > >2018.12.30 bzoj3027: [Ceoi2004]Sweet(生成函式+搜尋)

2018.12.30 bzoj3027: [Ceoi2004]Sweet(生成函式+搜尋)

傳送門
生成函式好題。
題意簡述:給出n個盒子,第 i i 個盒子裡有 m i m_i 顆相同的糖(但不同盒子中的糖不相同),問有多少種選法可以從各盒子中選出數量在 [

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}

然後把所有盒子的生成函式乘起來: 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 ( b ) f ( a 1 ) f(b)-f(a-1) f ( x ) f(x) 表示選出數量不超過 x x 的糖果的方案數。

左邊的一坨 x m x^m 的係數看成把 m m 拆成 n n 個自然數,為 C m + n 1 n 1 C_{m+n-1}^{n-1}
右邊的一坨爆搜即可。
然後對於右邊搜出來的 k x t kx^t ,假設當前要求數量不超過 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}
這樣就可以更新答案了。
注意模數的處理
程式碼:

#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;
}