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

题目描述

传送门

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

题解

组合问题,用到普通型生成函数
首先设 f(i) 表示至多吃掉i个的方案数,问题可以转化为求 f(b)f(a1) ,求 f(i) 实际上就是要求生成函数的 xi 项的系数
首先把生成函数的式子写出来,并化成闭形式

in(1+x+x2+...+xmi)=in1xmi+11x=in(1xmi+1)(1x)n

然后可以看出多项式变成 (1+x+x2+x3...)nin(1xmi+1)
第二个式子展开至多有 2n 个系数不为0的项,可以爆搜展开的每一项
而对于第一个式子,它的展开式的第i项的系数为 Cn1n+i1
那么假设现在爆搜到了一项 kxy ,当前要求的是 f(a) ,那么实际上就是要求生成函数第0..a-y项的系数和,也就是 k×(Cn1n1+Cn1n+Cn1n+1+...+Cn1n+ay1)
利用组合数的递推公式 Cji=Cj1i+Cj1i1 进行化简,系数和为 k×Cnn+ay
因为n比较小,求组合数的时候可以上下相消然后暴力枚举,而模数不是质数有可能不存在逆元,但是模数较小,可以在取模时先乘上 n!Mod 然后最后再除回去
时间复杂度 O(2nn)

代码

#include
#include
#include
#include
#include
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 (nreturn 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);
}

你可能感兴趣的:(题解,生成函数)