您的位置:首页 > 其它

[BZOJ3027][Ceoi2004]Sweet(生成函数)

2017-04-25 19:54 393 查看

题目描述

传送门

题目大意:有n种糖果,每种mi个,至少吃掉a个,至多b个,求吃掉糖果的方案数。

题解

组合问题,用到普通型生成函数

首先设f(i)表示至多吃掉i个的方案数,问题可以转化为求f(b)−f(a−1),求f(i)实际上就是要求生成函数的xi项的系数

首先把生成函数的式子写出来,并化成闭形式

∏in(1+x+x2+...+xmi)=∏in1−xmi+11−x=∏in(1−xmi+1)(1−x)n

然后可以看出多项式变成(1+x+x2+x3...)n∏in(1−xmi+1)

第二个式子展开至多有2n个系数不为0的项,可以爆搜展开的每一项

而对于第一个式子,它的展开式的第i项的系数为Cn−1n+i−1

那么假设现在爆搜到了一项k∗xy,当前要求的是f(a),那么实际上就是要求生成函数第0..a-y项的系数和,也就是k×(Cn−1n−1+Cn−1n+Cn−1n+1+...+Cn−1n+a−y−1)

利用组合数的递推公式Cji=Cj−1i+Cj−1i−1进行化简,系数和为k×Cnn+a−y

因为n比较小,求组合数的时候可以上下相消然后暴力枚举,而模数不是质数有可能不存在逆元,但是模数较小,可以在取模时先乘上n!⋅Mod然后最后再除回去

时间复杂度O(2n⋅n)

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define Mod 2004

int n,a,b,now,ans;
int m[15];
long long mul;

int C(int n,int m)
{
if (n<m) return 0;
long long ans=1,mod=mul*Mod;
for (int i=n-m+1;i<=n;++i)
ans=(long long)i%mod*(long long)ans%mod;
return (ans/mul)%Mod;
}
void dfs(int dep,int xi,int mi,int lim)
{
if (dep==n+1)
{
now=(now+xi*C(n+lim-mi,n)%Mod)%Mod;
return;
}
dfs(dep+1,xi,mi,lim);
dfs(dep+1,-xi,mi+m[dep]+1,lim);
}
int calc(int lim)
{
now=0;
dfs(1,1,0,lim);
return now;
}

int main()
{
scanf("%d%d%d",&n,&a,&b);
mul=1;for (int i=2;i<=n;++i) mul=mul*i;
for (int i=1;i<=n;++i) scanf("%d",&m[i]);
ans=calc(b)-calc(a-1);ans=(ans%Mod+Mod)%Mod;
printf("%d\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: