[ARC110D]Binomial Coefficient is Fun
壹、题目描述 ¶
贰、题解 ¶
我是大傻逼
我并没有想到什么比较好的做法,那么就直接上生成函数了。
定义 \(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;
}