今天刚学二项式反演,手感火热!
题目
JYY 带队参加了若干场 ACM/ICPC 比赛,带回了许多土特产,要分给实验室的同学们。
JYY 想知道,把这些特产分给 n 个同学,一共有多少种不同的分法?当然,JYY 不希望任何一个同学因为没有拿到特产而感到失落,所以每个同学都必须至少分得一个特产。
例如,JYY 带来了 2 袋麻花和 1 袋包子,分给 A 和 B 两位同学,那么共有 4 种不同的 分配方法:
A:麻花, B:麻花、包子
A:麻花、麻花, B:包子
A:包子, B:麻花、麻花
A:麻花、包子, B:麻花
思路
看一波二项式反演的基本公式:
对于这一题,我们设f(i)表示恰好有i个人没有特产的方案数,发现并不好求!
那么考虑反演,此处使用二项式反演是因为这是统计方案数的一种经典方法:设g(i)为钦定i个人没有特产的方案数
先求f(i),用排列组合里最基本的隔板法:对于f(k),把分给剩下n-k个人,剩下每个人可以分个,由此求得可得f(k):
套用二项式反演的公式,可得:
等量代换得:
不难发现,答案就是g(0):
其中,可以在计算时省去
快乐写代码吧!
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=1e6+9,M=3333,mod=1e9+7;
ll C[M][M];
ll n,m,a[N],f[N],ans,op;
void get()
{
for(int i=0;i<M;i++)
C[i][0]=1;
for(int i=1;i<M;i++)
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
int main()
{
get();
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++)
scanf("%lld",&a[i]);
for(int i=0;i<=n;i++)f[i]=C[n][i];
for(int i=0;i<=n;i++)
for(int j=1;j<=m;j++)
f[i]=f[i]*C[a[j]+n-i-1][n-i-1]%mod;
for(int i=0;i<=n;i++)
{
if(i&1)op=-1;
else op=1;
ans=(ans+op*f[i])%mod;
}
ans=(ans+mod)%mod;
printf("%lld",ans);
return 0;
}