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

来源:互联网 发布:淘宝开店上传照片要求 编辑:程序博客网 时间:2024/06/06 16:24

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;} 
0 0