[BZOJ3027][CEOI2004] Sweets

题目描述

(John)得到了(n)罐糖果。不同的糖果罐,糖果的种类不同(即同一个糖果罐里的糖果种类是相同的,不同的糖果罐里的糖果的种类是不同的)。第(i)个糖果罐里有(m_{i})个糖果。(John) 决定吃掉一些糖果,他想吃掉至少(a)个糖果,但不超过(b)个。问题是 (John)无法确定吃多少个糖果和每种糖果各吃几个。有多少种方法可以做这件事呢?

解题思路

生成函数题,考虑对于第(i)堆吃(j)个糖果的方案并写成封闭模式为

[F_i(x)=sum_{j=0}^{m_i}x^j=frac{1-x^{m_i+1}}{1-x} ]

那么对于吃(i)个的方案数为

[G(x)=prod_{i=1}^n F_i(x)=(1-x)^{-n}prod_{i=1}^n(1-x^{m_i+1}) ]

对于((1-x)^{-n})我们使用牛顿广义二项式展开可得(egin{aligned}(1-x)^{-n}=sum_{k=0}^{infty}dbinom{-n}{k}(-x)^k=sum_{k=0}^{infty}(-1)^kdbinom{n+k-1}{k}(-x)^k=sum_{k=0}^{infty}dbinom{n+k-1}{k}x^kend{aligned})

对于后面的柿子我们直接暴搜,怎么暴力怎么来就可以,对于前面的(egin{aligned}sum_{k=0}^{infty}dbinom{n+k-1}{k}x^kend{aligned}),我们考虑对答案的贡献,对于答案是(x^a)~(x^b)对应的系数,则设(prod_{i=1}^n(1-x^{m_i+1}))枚举到了(x^k),则为(egin{aligned}sum_{i=a-k}^{b-k}dbinom{n+i-1}{i}end{aligned}),展开后对其用加法公式(dbinom{n}{m}=dbinom{n-1}{m}+dbinom{n-1}{m-1})进行化简可得为(dbinom{n+b-k}{b-k}-dbinom{n+a-k-1}{a-k-1}),然后就可以做了,总体时间复杂度为(O(2^n))

细节注意

模数不是质数,组合数用定义进行计算

(Code)

#include<bits/stdc++.h>
    
#define LL long long
    
#define _ 0
    
using namespace std;
    
/*Grievous Lady*/
    
template <typename _n_> void mian(_n_ & _x_){
    _x_ = 0;int _m_ = 1;char buf_ = getchar();
    while(buf_ < '0' || buf_ > '9'){if(buf_ == '-')_m_ =- 1;buf_ = getchar();}
    do{_x_ = _x_ * 10 + buf_ - '0';buf_ = getchar();}while(buf_ >= '0' && buf_ <= '9');_x_ *= _m_;
}
    
const int kato = 20;

LL fac = 1 , ans , n , a , b , m[kato];

#define mod 2004

inline int c(int a , int b){
    if(a < b) return 0;
    LL res = 1 , tmp = fac * mod;
    for(int i = a - b + 1;i <= a;i ++){
        res = res * i % tmp;
    }
    return (res / fac) % mod;
}

void dfs(int x , int opt , int t){
    if(x == n + 1){
        ans = (ans + opt * (c(n + b - t , n) - c(n + a - t - 1 , n))) % mod;
        return;
    }
    dfs(x + 1 , opt , t) , dfs(x + 1 , - opt , t + m[x] + 1);
}

inline int Ame_(){
    mian(n) , mian(a) , mian(b);
    for(int i = 1;i <= n;i ++){
        mian(m[i]);
        fac = fac * i;
    }
    dfs(1 , 1 , 0);
    if(ans < 0) ans += mod;
    printf("%lld
" , ans);
    return ~~(0^_^0);
}
    
int Ame__ = Ame_();
    
int main(){;}

/*
2 1 3
3
5
*/

-----(mathcal{Ame\_\_})

原文地址:https://www.cnblogs.com/Ame-sora/p/13603968.html