CF 451E Devu and Flowers
可重集的排列数 + 容斥原理
对于 \(\{A_1 * C_1, A _2 * C_2, \cdots, A_n * C_n\}\)这样的集合来说,
设 \(N = \sum_{i = 1} ^ n A_i\), 要在这个集合中取出 \(M\) 个元素来,这样的方案数是:
\[C _ {N+M-1}^{N-1} - \sum _ {i =1} ^ n {C_{N+M-A_i - 2}^{N-1}} + \sum _ {1\leq i < j \leq n} ^ n {C_{N+M - A_i - A_j -3}^{N-1}} \cdots +(-1)^N * C_{N+M-\sum_{i = 1}^n {c_i} - (N+1)}^{N-1}
\]
我们可以通过枚举 \(1 \sim 2 ^ N - 1\)的数来表示这些组合数,对于一个数 \(x\) 来说,如果它的第 \(p\) 位上是 1 ,就表示减去 \(A_p\) ,若一共有 \(q\) 位是 1 ,则 一共减去 \(q\) 个元素,
在计算组合数 \(C_M^N\) 的时候,我们可以先计算 \(A_M^N\), 在乘上 \((N- 1)!\)的逆元.
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int MOD = 1000000007;
ll n, inv[100];
ll num[50], m, ans;
ll C(ll x, ll y) {
if(x < 0 || y < 0 || x < y) return 0;
x %= MOD;
if(!y) return 1;
if(!x) return 0;
ll ans = 1ll;
for(int i = 0; i < y; i++) {
(ans *= (x - i) ) %= MOD;
}
(ans *= inv[y] ) %= MOD;
return ans;
}
int main() {
inv[0] = inv[1] = 1;
for(int i = 2; i <= 30; i++) inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
for(int i = 2; i <= 30; i++) (inv[i] *= inv[i - 1]) %= MOD;
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> num[i];
ans += C(m + n - 1, n - 1);
for(int i = 1; i < (1 << n); i++) {
ll t = m + n;
int p = 0;
for(int j = 0; j < n; j++) {
if((i >> j) & 1) {
p++;
t -= num[j + 1];
}
}
t -= p + 1;
if(p & 1) {
(ans -= C(t, n - 1)) %= MOD;
}else (ans += C(t, n - 1)) %= MOD;
}
cout << (ans + MOD) % MOD << endl;
return 0;
}