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

4710: [Jsoi2011]分特产

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


对于每种物品是独立的,就是分成n组可以为空,然后可以用乘法原理合起来
容斥容斥
\[ 每个同学至少一个=所有方案数-\ge 1个同学没有+\ge 2 个同学没有-... \]
\(\ge i\)个同学没有,我们拿出来i个同学\(\binom{n}{i}\)个方案,剩下就是每种物品分成\(n-i\)组再乘起来罢了...


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
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<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
 
 
int 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();
}
posted @ 2017-03-25 12:58  Candy?  阅读(...)  评论(... 编辑 收藏