bzoj 4710

非常简单的组合数学+容斥原理

直接计算不好计算,我们用容斥原理计算:所有人随便选-至少有一个人没有礼物+至少有两个人没有礼物...

假设我们有$i$个人没有礼物,那么方案数为$C_{n}^{i}\prod_{j=1}^{m}C_{a[j]+n-i-1}^{n-i-1}$

后面那个组合数的含义是对于每一种特产,这种特产共有$a[j]$个,将其分给$n-i$个人,允许有人拿不到的方案数

可以理解成共有$a[j]+n-i-1$个位置,在其中插入$n-i-1$个隔板的方案数

因此总答案即为$\sum_{i=0}^{n}(-1)^{i}C_{n}^{i}\prod_{j=1}^{m}C_{a[j]+n-i-1}^{n-i-1}$

贴代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
const ll mode=1000000007;
ll n,m;
ll mul[1000005];
ll inv[1000005];
ll minv[1000005];
ll a[1000005];
void init()
{
    inv[0]=inv[1]=minv[0]=minv[1]=mul[0]=mul[1]=1;
    for(int i=2;i<=1000000;i++)
    {
        inv[i]=(mode-mode/i)*inv[mode%i]%mode;
        mul[i]=mul[i-1]*i%mode;
        minv[i]=minv[i-1]*inv[i]%mode;
    }
}
ll C(ll x,ll y)
{
    if(x<=y)return 0;
    return mul[x]*minv[y]%mode*minv[x-y]%mode;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    init();
    for(int i=1;i<=m;i++)scanf("%lld",&a[i]);
    ll ans=0,f=1;
    for(int i=0;i<=n;i++)
    {
        ll sum=1;
        for(int j=1;j<=m;j++)sum=(sum*C(a[j]+n-i-1,n-i-1))%mode;
        ans=((ans+f*C(n,i)%mode*sum%mode)%mode+mode)%mode;
        f=-f;
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zhangleo/p/10969784.html