BZOJ 4710: [Jsoi2011]分特产 [容斥原理]

时间:2023-03-10 01:25:16
BZOJ 4710: [Jsoi2011]分特产 [容斥原理]

4710: [Jsoi2011]分特产

题意:m种物品分给n个同学,每个同学至少有一个物品,求方案数


对于每种物品是独立的,就是分成n组可以为空,然后可以用乘法原理合起来

容斥容斥

\[每个同学至少一个=所有方案数-\ge 1个同学没有+\ge 2 个同学没有-...
\]

\(\ge i\)个同学没有,我们拿出来i个同学\(\binom{n}{i}\)个方案,剩下就是每种物品分成\(n-i\)组再乘起来罢了...


```cpp
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=5005, P=1e9+7;
inline int read(){
char c=getchar();int x=0,f=1;
while(c'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&cint n, m, c[N];

ll inv[N], fac[N], facInv[N];

inline ll C(int n, int m) {return fac[n]facInv[m]%P facInv[n-m]%P;}

inline ll f(int c, int n) {return C(n+c-1, n-1);}

void solve() {

ll ans=0;

for(int i=0; i<=n; i++) {

ll t=1;

for(int j=1; j<=m; j++) t=(t * f(c[j], n-i))%P;

t = t
C(n, i)%P;

(ans += (i&1) ? -t : t) %=P;

}

printf("%lld\n",(ans+P)%P);

}

int main() {

//freopen("in","r",stdin);

n=read(); m=read(); int lim=0;

for(int i=1; i<=m; i++) c[i]=read(), lim=max(lim, c[i]);

inv[1]=1; fac[0]=facInv[0]=1;

for(int i=1; i<=n+lim; i++) {

if(i!=1) inv[i] = (P-P/i)
inv[P%i]%P;

fac[i] = fac[i-1]i%P;

facInv[i] = facInv[i-1]
inv[i]%P;

}

solve();

}