luogu 5505 [JSOI2011]分特产 广义容斥
共有 $m$ 种物品,每个物品 $a[i]$ 个,分给 $n$ 个人,每个人至少要拿到一件,求方案数.
令 $f[i]$ 表示钦定 $i$ 个没分到特产,其余 $(n-i)$ 个人随便选的方案数,$g[i]$ 表示恰好 $i$ 个没分到特产的方案数.
按照我们之前讲的,有 $f[k]=\sum_{i=k}^{n}\binom{k}{i}g[i]\Rightarrow g[k]=\sum_{i=k}^{n}(-1)^{i-k}\binom{i}{k}f[i]$
而根据定义,$f[i]=\binom{n}{i}\times \prod_{j=1}^{m}\binom{a[j]+n-i-1}{n-i-1}$
所以先预处理 $f[i]$,然后求 $g[0]$ 就好了(恰好 $0$ 个人没分到特产的方案数)
code:
#include <bits/stdc++.h> #define N 10005 #define LL long long using namespace std; const LL mod=1000000007; void setIO(string s) { string in=s+".in"; string out=s+".out"; freopen(in.c_str(),"r",stdin); } int a[N]; LL fac[N],inv[N],f[N],g[N]; LL qpow(LL x,LL y) { LL tmp=1ll; for(;y;y>>=1,x=x*x%mod) if(y&1) tmp=tmp*x%mod; return tmp; } LL Inv(LL x) { return qpow(x,mod-2); } LL C(int x,int y) { return fac[x]*inv[y]%mod*inv[x-y]%mod; } int main() { // setIO("input"); int i,j,n,m; fac[0]=inv[0]=1ll; for(i=1;i<N;++i) fac[i]=fac[i-1]*1ll*i%mod,inv[i]=Inv(fac[i]); scanf("%d%d",&n,&m); for(i=1;i<=m;++i) scanf("%d",&a[i]); for(i=0;i<=n;++i) { f[i]=C(n,i); for(j=1;j<=m;++j) (f[i]=f[i]*C(a[j]+n-i-1,n-i-1)%mod)%=mod; } for(i=0;i<=n;++i) { (g[0]+=qpow(-1,i)*f[i]%mod+mod)%=mod; } printf("%lld\n",g[0]); return 0; }