[ARC110D]Binomial Coefficient is Fun

壹、题目描述 ¶

传送门 to atcoder.

贰、题解 ¶

我是大傻逼

我并没有想到什么比较好的做法,那么就直接上生成函数了。

定义 \(f(i,j)\) 表示前 \(i\) 位,选的数字和为 \(j\) 的总贡献之和,那么存在转移:

\[f(i,j)=\sum_{k=0}^jf(i-1,k){j-k\choose a_i} \]

我们不难发现,\(f(i)\)​ 实际上就是 \(f(i-1)\)​ 与某个函数 \(g\)​​ 卷起来的结果。考虑使用生成函数表示这个转移。

考虑定义

\[G(i,x)=\sum_{n=0}^\infty {n\choose a_i}x^n \\ F(i,x)=\sum_{n=0}f(i,n)x^n \]

那么就有

\[F(i,x)=F(i-1,x)*G(i,x) \]

我们可以同样将 \(F(i-1,x)\) 展开,它肯定是 \(F(i-2,x)\)\(G(i-1,x)\) 卷起来,最后,我们不难发现

\[F(n,x)=\prod_{i=1}^nG(i,x) \]

显然,最终的答案为

\[Ans=\sum_{i=0}^m[x^i]F(n,x) \]

我们尝试搞清楚每一个 \(G(i,x)\),然后再将他们组合起来。

考虑到 \(G(i,x)=\sum_{n=0}^\infty{n\choose a_i}x^n\)​,我们考虑一个更一般的形式,设 \(S(x)=\sum_{i=0}^\infty{i\choose p}x^i\)​,其中 \(p\)​ 为一个任意整数。

显然,\(S(x)\)​ 的前 \(p\)​ 项系数都是 \(0\)​,我们考虑提出一个 \(x^p\)​,让 \(i\)​ 又可以从 \(0\)​ 开始,这样,我们有

\[\begin{align} S(x)&=x^p\sum_{i=0}^\infty {i+p\choose p}x^i\tag{1} \\ &=x^p\sum_{i=0}^\infty {i+p\choose i}x^i\tag{2} \\ &=x^p\sum_{i=0}^\infty{-p-1\choose i}(-x)^i\tag{3} \\ &=x^p(1-x)^{-p-1}\tag{4} \\ &={x^p\over (1-x)^{p+1}}\tag{5} \end{align} \]

注:\((2)\rightarrow (3)\) 使用组合数的广义定义,即

\[{n\choose m}={n^{\underline m}\over m!} \]

为了能使用二项式定理也是豁出去了。

计算 \(G(i)\),只需将 \(p\) 换成 \(a_i\) 就行了。

由于最后是所有的 \(G\) 乘起来,我们记 \(s=\sum a_i\),那么有

\[F(x)={x^s\over (1-x)^{s+n}} \]

\(i\)​ 项系数为 \({(i-s)+s+n-1\choose s+n-1}={i+n-1\choose s+n-1}\)​​.​

将其带入答案,那么我们可以得到

\[Ans=\sum_{i=0}^m{i+n-1\choose s+n-1}={m+n\choose s+n} \]

令人惊讶的答案!时间复杂度 \(\mathcal O(s+n)\).

叁、参考代码 ¶

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

#define NDEBUG
#include<cassert>

namespace Elaina{
    #define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
    #define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
    #define fi first
    #define se second
    #define mp(a, b) make_pair(a, b)
    #define Endl putchar('\n')
    #define mmset(a, b) memset(a, b, sizeof a)
    // #define int long long
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    typedef pair<ll, ll> pll;
    template<class T>inline T fab(T x){ return x<0? -x: x; }
    template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
    template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
    template<class T>inline T readin(T x){
        x=0; int f=0; char c;
        while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
        for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
        return f? -x: x;
    }
    template<class T>inline void writc(T x, char s='\n'){
        static int fwri_sta[1005], fwri_ed=0;
        if(x<0) putchar('-'), x=-x;
        do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
        while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
        putchar(s);
    }
}
using namespace Elaina;

const int Mod=1e9+7;
const int maxn=2000;

inline int qkpow(int a, int n){
    int ret=1;
    for(; n>0; n>>=1, a=1ll*a*a%Mod)
        if(n&1) ret=1ll*ret*a%Mod;
    return ret;
}

int n, m, s;

inline void input(){
    n=readin(1), m=readin(1);
    for(int i=1; i<=n; ++i) s+=readin(1);
}

inline int C(int n, int m){
    int up=1, down=1;
    for(int i=1; i<=m; ++i){
        up=1ll*up*(n-i+1)%Mod;
        down=1ll*down*i%Mod;
    }
    return 1ll*up*qkpow(down, Mod-2)%Mod;
}

signed main(){
    input();
    if(m+s<n+s) return writc(0), 0;
    writc(C(m+n, n+s));
    return 0;
}
posted @ 2021-07-27 21:45  Arextre  阅读(109)  评论(0编辑  收藏  举报