Devu and Flowers

题解

有n个花坛,要选s支花,每个花坛有f[i]支花,同一个花坛的花颜色相同,不同花坛的花颜色不同,问说可以有多少种组合。

2^n的状态,枚举某些花坛的花超过了,剩下的用隔板法计算个数,再加个容斥原理就行了

————————————————————————————————————————————-

看来我应该写详细点

首先隔板法sum个球放进n个盒子中允许盒子为空的方案是C(sum+n-1,n-1)

但是由于这里有个限制,即f[i],这样计数的话某些花会超出其个数

所以我们应该2^n枚举某些花超出了个数,比如确定i超出个数其它不确定的方案C(sum-f[i]-1+n-1,n-1)

即sum-=f[i]+1(因为允许箱子为空一开始要人为加入一个球)

这里再加个容斥原理+超0个的-超1个的+超2个的……

 

还有要用到乘法逆元求C和lucas定理

 

根据费马小定理a^(p-1)=1(mod p),a为质数

a^(p-2)=a^(-1)(mod p),那么a^(p-2)就是a在modp意义下的逆元

所以可以将C的公式上下分别得出取模后的乘积s1,s2,将s1乘s2的逆元

 

lucas定理:C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)

前半部分继续递归用lucas定理,后半部分直接求

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll pow_mod(ll a,ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

ll C(int n,int m) {
    if (n < m) return 0;
    if (m > n - m) m = n - m;
    ll s1 = 1, s2 = 1;
    for (ll i = 0; i < m; i++) {
        s1 = s1 * (n - i) % mod;
        s2 = s2 * (i + 1) % mod;
    }
    return s1 * pow_mod(s2, mod - 2) % mod;
}

ll lucas(ll n,ll m) {
    if (m == 0) return 1;
    return C(n % mod, m % mod) * lucas(n / mod, m / mod) % mod;
}

int n;
ll s,f[25];

ll solve() {
    ll ans = 0;
    for (int i = 0; i < 1 << n; i++) {
        ll sign = 1, sum = s;
        for (int j = 0; j < n; j++) {
            if (i >> j & 1) {
                sum -= f[j]+1;
                sign *= -1;
            }
        }
        if (sum < 0) continue;
        ans += sign * lucas(sum + n - 1, n - 1);
        ans %= mod;
    }
    return (ans + mod) % mod;
}

int main() {
    scanf("%d%lld", &n, &s);
    for (int i = 0; i < n; i++) {
        scanf("%lld", &f[i]);
    }
    printf("%lld\n", solve());
    return 0;
}

  

posted @ 2019-09-18 19:33  Snow_in_winer  阅读(148)  评论(0编辑  收藏  举报