cf451E Devu and Flowers 卢卡斯定理+容斥定理

时间:2022-12-18 22:04:25

题目:http://codeforces.com/problemset/problem/451/E

题意:有n个盒子(n<=20),每个盒子中有10^12个小球,现从每个盒子中取出若干球(可为0),求共取出s个小球(s<=10^14)的方案数。


组合数学问题,求C(n,m).但n,m过大时,可用卢卡斯定理.

卢卡斯定理:C(n,m) %p = C(n/p,m/p) * C(n%p,m%p)


从n个盒子中取出s个球的方案数,相当于插板,即 C(s+n-1,n-1).注意这是没有限制条件的情况。

还要考虑哪些盒子取超了。

违法情况和 = 1个盒子取超的情况数 - 2个的 + 3个的 ....(容斥定理)

然后拿总方案数减去违法的,就是最终结果。总方案数 = C(s+n-1,n-1)


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
const ll mod = 1e9+7;
const ll MOD = 1e9+7;

ll k_pow(ll a,ll n,ll p){
    ll ans = 1ll, mo = a;
    while(n){
        if(n&1) ans = (ans * mo)%p;
        mo = (mo * mo)%p;
        n /= 2;
    }
    return ans;
}

ll cal(ll n,ll m,ll p){
    if(m>n) return 0;
    ll ans = 1ll;
    ll wt = 1ll;
    if(m>n-m) m = n-m;
    for(int i=1;i<=m;i++){
        ans = (ans * (n-i+1))%p;
        wt = (wt * i)%p;
    }
    wt = k_pow(wt,p-2,p);
    ans = (ans * wt)%p;
    return ans;
}


ll lucas(ll n,ll m,ll p){
    if(m==0) return 1;
    return ( lucas(n/p,m/p,p) * cal(n%p,m%p,MOD) )%p;
}


int n;
ll s,f[30];
int main(){
    while(scanf("%d%I64d",&n,&s)!=EOF){
        ll sum = 0;
        for(int i=0;i<n;i++){
            scanf("%I64d",&f[i]);
            sum += f[i];
        }
        ll ans = 0;
        for(int st=0;st<(1<<n);st++){
            int fl = 1;
            ll ts = s;
            for(int i=0;i<n;i++) if((1<<i)&st){
                fl *= -1;
                ts -= f[i]+1;
            }
            if(ts<0) continue;
            ans = (ans + fl*lucas(ts+n-1,n-1,MOD))%MOD;
        }
        ans = (ans + MOD)%MOD;
        cout<<ans<<endl;
    }
    return 0;
}