CF451E Devu and Flowers

多重集求组合数,注意到\(n = 20\)所以可以用\(2 ^ n * n\)的容斥来写。

如果没有限制那么答案就是\(C(n + s - 1, n - 1)\)。对每一个限制依次考虑,加上有一种选多的,减去有两种选多的,以此类推。

由于\(n <= 20\),所以组合数事实上是可以\(O(N)\)求的=_=

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int Mod = 1000000007;
const int N = (1 << 20) + 5;

int add (int x, int y) {return (((0ll + (x % Mod) + (y % Mod)) % Mod) + Mod) % Mod;}
int mul (int x, int y) {return (((1ll * (x % Mod) * (y % Mod)) % Mod) + Mod) % Mod;}

int n, s, f[21], fac[21], inv[21];

int C (int n, int m) {
    int res = 1;
    for (int i = n - m + 1; i <= n; ++i) res = mul (res, i);
    res = mul (res, inv[m]);
    return res; 
}

int fpow (int x, int y) {
    int res = 1;
    while (y) {
        if (y & 1) {
            res = mul (res, x);
        }
        x = mul (x, x);
        y >>= 1;
    }
    return res;
}

signed main () {
    cin >> n >> s;
    for (int i = 1; i <= n; ++i) {
        cin >> f[i];
    }
    fac[0] = 1;
    for (int i = 1; i <= n; ++i) {
        fac[i] = mul (fac[i - 1], i);
    }
    inv[n] = fpow (fac[n], Mod - 2);
    for (int i = n; i >= 1; --i) {
        inv[i - 1] = mul (inv[i], i);
    }
    int ans = C (n + s - 1, n - 1);
    for (int i = 1; i < (1 << n); ++i) {
        int w = 0, del = 0, have_wei = 0;
        for (int j = 0; j < n; ++j) {
            if (i & (1 << j)) {
                have_wei++;
                del += f[j + 1] + 1;
            }
        }
        w = have_wei & 1 ? -1 : + 1;
        if (s >= del) {
            ans = add (ans, w * C (n + s - 1 - del, n - 1));
        }
    }
    cout << ans << endl;
}
上一篇:乘法逆元


下一篇:AT1983 BBQ Hard