其他分享
首页 > 其他分享> > acwing214 Devu和鲜花 多重集组合数,容斥,卢卡斯定理

acwing214 Devu和鲜花 多重集组合数,容斥,卢卡斯定理

作者:互联网

题目链接https://www.acwing.com/problem/content/216/

Devu有N个盒子,第i个盒子中有Ai枝花。

同一个盒子内的花颜色相同,不同盒子内的花颜色不同。

Devu要从这些盒子中选出M枝花组成一束,求共有多少种方案。

若两束花每种颜色的花的数量都相同,则认为这两束花是相同的方案。

结果需对10^9+7取模之后方可输出。

输入格式

第一行包含两个整数N和M。

第二行包含N个空格隔开的整数,表示A1,A2,…,AN。

输出格式

输出一个整数,表示方案数量对10^9+7取模后的结果。

数据范围

1≤N≤20,
0≤M≤10^14,
0≤Ai≤10^12

输入样例:

3 5
1 3 2

输出样例:

3

题解:

 参考李煜东大佬的算法竞赛进阶指南。

算组合数的时候m较大,n较小。  C(n+m-1,n-1)=n! / (n!(n-m)!) =((n+m-1)*(n+m-2)*...(m+1) /(1*2*...(n-1))

可以先算出((n+m-1)*(n+m-2)*...(m+1),然后乘(n-1)!的逆元。

可以用卢卡斯定理,先对n+m-1 对1e9+7取模,再计算组合数,就不会超出long long 范围

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll a[22],ni[22];
ll ksm(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}
ll C(ll y,ll x){
    if(x<0||y<0||y<x) return 0;
    y%=mod;
   ll ans=1;
    for(int i=y-x+1;i<=y;i++) ans=ans*i%mod;
    for(int i=1;i<=x;i++) ans=ans*ni[i]%mod;
    return ans;
}
void init(){
    for(int i=1;i<=20;i++) ni[i]=ksm(i,mod-2);
}
int main(){
    init();
    ll n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++) scanf("%lld",&a[i]);
    ll ans=C(n+m-1,n-1);
    for(int i=1;i<(1<<n);i++){
        ll t=n+m;
        int p=0;
        for(int j=0;j<n;j++){
            if(i>>j&1){
                p++;
                t-=a[j];
            }
        }
        t-=(p+1);
        if(p&1) ans=(ans-C(t,n-1)+mod)%mod;
        else ans=(ans+C(t,n-1))%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

 

标签:10,盒子,多重集,ll,容斥,long,acwing214,ans,mod
来源: https://blog.csdn.net/weixin_42757232/article/details/100056764