Acwing 214. Devu和鲜花(容斥原理)

输入样例:

3 5
1 3 2

输出样例:

3

解题思路:首先我们要先考虑问题的简化版本,即如果每个盒子里的花数量无限的话,我们假设第i个盒子中选出xi朵花,那么最终的答案就应该是满足x1+x2+x3+……+xn=m的解的个数,这里我们可以想到用隔板法去解决,但是每个xi>=0,隔板法不能取到0,所以我们为每一个xi+1变成yi,yi>=1,现在就是从n+m-1个空中选择n-1个空 将n+m朵花分成n组,但是这个问题现在是有限制条件的,即每个xi<=ai,这就是我们进行容斥原理所要用到的条件,我们如果正着算会发现比较难算,我们秉持着正难则反的原理,反向思考,我们一开始的条件是x1<=a1&&x2<=a2&&……&&xn<=an,对这个式子进行取反得到x1>a1||x2>a2||……||xn>an,我们设si表示对于xi满足xi>ai的条件,我们用总的情况数即C(n+m-1,n-1)减去上面这种情况,上面这种情况可以用容斥原理来解决,举个例子对于满足条件s2,s3的情况数量我们可以这么求,先从总数中减去在2号箱子,3号箱子分别拿走a2+1,a3+1朵花,然后在剩下的花中利用我们上面说到的隔板法进行求解,公式是C(n+m-1-(a2+1)-(a3+1),n-1),如果不知道容斥原理的可以自行百度。

上代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=20,mod=1e9+7;
long long a[N],n,m,down=1;
long long ksm(long long a,long long b)
{
    long long res=1;
    while(b)
    {
        if(b&1)
        res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res;
}
long long C(long long a,long long b)
{
    if(a<b)
    return 0;
    long long res=0;
    long long up=1;
    for(long long i=a;i>a-b;i--)
    up=i%mod*up%mod;
    return up%mod*down%mod;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
    cin>>a[i];
    for(int i=1;i<n;i++)
    down=(down*i)%mod;
    down=ksm(down,mod-2);//因为所有求组合数的的分母都是一样的,所以只需要求一遍即可
    long long ans=0;
    //枚举所有状态,第j位为1表示第j个数满足条件sj
    for(int i=0;i<(1<<n);i++)
    {
        long long a1=m+n-1,b=n-1;
        int sign=1;
        for(int j=0;j<n;j++)
        {
            if((i>>j)&1)
            {
                sign=sign*(-1);
                a1-=a[j]+1;
            }
        }
        ans=(ans+C(a1,b)*sign%mod+mod)%mod;
    }
    cout<<(ans+mod)%mod<<endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值