CodeForces 451 E.Devu and Flowers(组合数学)

时间:2022-12-19 00:03:50

Description
有n个箱子,要选s支花,每个箱子有f[i]支花。同一个箱子的花颜色相同,不同箱子的花颜色不同,问说可以有多少种组合
Input
第一行输入两个整数n和s表示箱子数量和要选的花的数量,第二行n个整数表示每个花坛中花的数量
Output
输出从这n个箱子中选取s支花的方法数
Sample Input
3 5
1 3 2
Sample Output
3
Solution
首先隔板法sum个球放进n个盒子中允许盒子为空的方案是C(sum+n-1,n-1),但是由于这里有个限制,即f[i],这样计数的话某些花会超出其个数,所以我们应该2^n枚举从哪些箱子取的花的数量超出了f[i],比如确定i超出个数其它不确定的方案C(sum-f[i]-1+n-1,n-1),即sum-=f[i]+1(因为允许箱子为空一开始要人为在超了的箱子中加入一个球)
然后就是容斥原理ans=超0个-超1个+超2个……
其中求组合数C(n,m)时因为n和m较大故需用到lucas定理
lucas定理:C(n,m)%p=C(n/p,m/p)*C(n%p,m%p),前半部分继续递归用lucas定理,后半部分用组合数直接求
Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const ll mod=1000000007ll;
ll mod_pow(ll a,ll b,ll p)//快速幂,求a^b(mod p) 
{
    ll ans=1;
    a%=p;
    while(b)
    {
        if(b&1)ans=ans*a%p;
        b>>=1;
        a=a*a%p;
    }
    return ans;
}
ll C(ll n,ll m)//求组合数C(n,m) 
{
    if(n<m)
        return 0;
    if(m>n-m)
        m=n-m;
    ll a=1,b=1;
    for(ll i=0;i<m;i++)
    {
        a=a*(n-i)%mod;
        b=b*(i+1)%mod;
    }
    return a*mod_pow(b,mod-2,mod)%mod;//费马小定理a^p=a(mod p) 
}
ll lucas(ll n,ll m)//lucas定理求C(n,m) 
{
    ll ret=1;
    while(n&&m)
    {
        ll a=n%mod,b=m%mod;
        if(a<b) 
            return 0;
        ret=ret*C(a,b)%mod;
        n/=mod;
        m/=mod;
    }
    return ret;
}
ll n,s,f[22];
int main()
{
    while(~scanf("%I64d%I64d",&n,&s))
    {
        for(ll i=0;i<n;i++)
            scanf("%I64d",&f[i]);
        ll ans=0;
        for(ll i=0;i<(1<<n);i++)//2^n枚举所有状态 
        {
            ll flag=1,sum=s;
            for(ll j=0;j<n;j++)//枚举所有箱子判断其是否会超 
                if(i&(1<<j))// 
                {
                    sum-=f[j]+1;//这个箱子超了则需在其中人为放一个球后再用隔板法 
                    flag*=-1;//flag判断容斥时是加还是减 
                }
            if(sum<0)//从超了的箱子中取的花的数量已经超过所需 
                continue;
            ans+=flag*lucas(sum+n-1,n-1);//容斥 
            ans%=mod;
        }
        ans=(ans+mod)%mod;
        printf("%I64d\n",ans);
    }
    return 0;
}