可重集的排列数 + 容斥原理
对于 ({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 _ {1leq 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;
}