#数论,组合,容斥原理,lucas定理,乘法逆元#洛谷 CF451E Devu and Flowers

题目

n n n种颜色,每种颜色有 a i a_i ai枝花,现挑出 m m m朵,使没有颜色完全相同的方案


分析

可以发现,这道题是求多重集的组合数,根据容斥原理也就是
C k + r − 1 k − 1 − ∑ i = 1 k C k + r − n i − 2 k − 1 + ∑ 1 ≤ i &lt; j ≤ k C k + r − n i − n j − 3 k − 1 − ⋯ + ( − 1 ) k C k + r − ∑ i = 1 k n i − ( k + 1 ) C_{k+r-1}^{k-1}-\sum_{i=1}^kC_{k+r-n_i-2}^{k-1}+\sum_{1\leq i&lt;j\leq k}C^{k-1}_{k+r-n_i-n_j-3}-\cdots+(-1)^kC_{k+r-\sum_{i=1}^kn_i-(k+1)} Ck+r1k1i=1kCk+rni2k1+1i<jkCk+rninj3k1+(1)kCk+ri=1kni(k+1)
关于优化的方面,因为选择的数量特别大,所以说需要用二进制优化,还是比较简单去想的,对于判断越界可以用lucas定理@my blog古代猪文,关于组合数的求法可以用乘法逆元


代码

#include <cstdio>
#define rr register
#define mod 1000000007
long long m,a[20],ans; int n,inv[20];
inline int ksm(int x,int y){//快速幂
    int ans=1;
    while (y){
        if (y&1) ans=(long long)ans*x%mod;
        x=(long long)x*x%mod; y>>=1;
    }
    return ans;
}
inline int c(long long n,int m){
    if (n<0||m<0||n<m) return 0;//不可能存在答案
    if (!n||!m) return 1;//特判
    int ans=1;
    for (rr int i=0;i<m;++i)
        ans=(long long)ans*(n-i)%mod*inv[i]%mod;//求组合数
    return ans;
}
int main(){
    scanf("%d%lld",&n,&m);
    for (rr int i=0;i<n;++i) scanf("%lld",&a[i]),inv[i]=ksm(i+1,mod-2);//乘法逆元
    for (rr int x=0;x<1<<n;++x){
        if (!x) ans=(ans+c((n+m-1)%mod,n-1))%mod;//不考虑重复的状况
        else{
            long long t=n+m; int p=0;
            for (rr int i=0;i<n;++i)
            if (x>>i&1) p++,t-=a[i];//记录1的个数
            t-=p+1;
            if (p&1) ans=(ans-c(t%mod,n-1))%mod;//求答案
            else ans=(ans+c(t%mod,n-1))%mod;//可能要加回去(容斥定理)
        }
    }
    printf("%lld",(ans+mod)%mod);//算下来可能会出现负数
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值