[JSOI2011] 分特产
题目描述
JYY 带队参加了若干场 ACM/ICPC \text{ACM/ICPC} ACM/ICPC 比赛,带回了许多土特产,要分给实验室的同学们。
JYY 想知道,把这些特产分给 n n n 个同学,一共有多少种不同的分法?当然,JYY 不希望任何一个同学因为没有拿到特产而感到失落,所以每个同学都必须至少分得一个特产。
例如,JYY 带来了
2
2
2 袋麻花和
1
1
1 袋包子,分给
A
A
A 和
B
B
B 两位同学,那么共有
4
4
4 种不同的
分配方法:
A A A:麻花, B B B:麻花、包子
A A A:麻花、麻花, B B B:包子
A A A:包子, B B B:麻花、麻花
A A A:麻花、包子, B B B:麻花
输入格式
输入数据:
第一行是同学的数量 n n n 和特产的数量 m m m。
第二行包含 m m m 个整数,表示每一种特产的数量。
n , m n, m n,m 不超过 1000 1000 1000 ,每一种特产的数量不超过 1000 1000 1000。
输出格式
输出一行,不同分配方案的总数。
由于输出结果可能非常巨大,你只需要输出最终结果
m
o
d
1
0
9
+
7
\bmod\ {10^9+7}
mod 109+7 的数值就可以了。
样例 #1
样例输入 #1
5 4
1 3 3 5
样例输出 #1
384835
Solve:
每个同学都要分到特产,直接求并不是很好求,考虑容斥。
设有
n
n
n 种性质,其中第
i
i
i 种性质
a
i
a_i
ai 表示:至少有
i
i
i 个同学拿不到特产,同时设
N
(
a
)
N(a)
N(a) 表示满足性质a的方案数,定义
N
(
1
)
N(1)
N(1) 为全集的数量.
则所求为
N
(
∏
i
=
1
n
(
1
−
a
i
)
)
=
∑
i
=
0
n
(
−
1
)
i
∑
1
≤
j
1
<
j
2
<
⋯
<
j
i
≤
n
N
(
a
j
1
a
j
2
…
a
j
i
)
N(\prod_{i=1}^n(1-a_i))\\=\sum_{i=0}^n(-1)^i\sum_{1\leq j1<j2<\dots<j_i\leq n}N(a_{j_1}a_{j_2}\dots a_{j_i})
N(i=1∏n(1−ai))=i=0∑n(−1)i1≤j1<j2<⋯<ji≤n∑N(aj1aj2…aji)由于同学之间相互等价,故上式继续化简:
=
∑
i
=
0
n
(
−
1
)
i
(
n
i
)
N
(
a
1
a
2
…
a
i
)
~~~~~~~~~~~~~=\sum_{i=0}^n(-1)^i{n \choose i}N(a_1a_2\dots a_i)
=∑i=0n(−1)i(in)N(a1a2…ai)
设
f
(
i
)
=
N
(
a
1
a
2
…
a
i
)
~~~~~~~~f(i) = N(a_1a_2\dots a_i)
f(i)=N(a1a2…ai)
原式
=
∑
i
=
0
n
(
−
1
)
i
(
n
i
)
f
(
i
)
~~~~~~~~=\sum_{i=0}^n(-1)^i{n \choose i}f(i)
=∑i=0n(−1)i(in)f(i)
考虑
f
(
i
)
f(i)
f(i) 的求法:
对每一种特产采用隔板法,假设某种特产有
s
s
s 件,根据隔板法,其对应的分配方案数为:
(
n
−
i
+
s
−
1
n
−
i
−
1
)
n-i+s-1 \choose n-i-1
(n−i−1n−i+s−1)
故
f
(
i
)
=
∏
j
=
1
m
(
n
−
i
+
A
j
−
1
n
−
i
−
1
)
f(i)=\prod_{j=1}^m {n-i+A_j-1 \choose n-i-1}
f(i)=j=1∏m(n−i−1n−i+Aj−1)
通过先预处理出
f
f
f 代入最终的表达式中即可得到答案。
时间复杂度:
O
(
n
m
)
O(nm)
O(nm)
Code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
const int N = 2010;
int n, m, f[N], fac[N], infac[N], a[N], ans;
int qpow(int x, int n, const int &mod) {
int res = 1;
while (n) {
if (n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
} return res;
}
void init(int n = N - 10) {
fac[0] = infac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = fac[i - 1] * i % mod;
infac[n] = qpow(fac[n], mod - 2, mod);
for (int i = n - 1; i >= 1; i--) {
infac[i] = infac[i + 1] * (i + 1) % mod;
}
assert(infac[1] == 1);
}
int binom(int a, int b) {
if (a < b) return 0;
return fac[a] * infac[b] % mod * infac[a - b] % mod;
}
signed main() {
init();
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> a[i];
}
for (int i = 0; i <= n; i++) {
int r = n - i;
f[i] = binom(n, i);
for (int j = 1; j <= m; j++) {
f[i] *= binom(a[j] + r - 1, r - 1) % mod;
f[i] %= mod;
}
}
for (int i = 0; i <= n; i++) {
int sg = i % 2 == 0 ? 1 : -1;
ans += sg * f[i];
ans %= mod;
}
cout << (ans + mod) % mod << "\n";
return 0;
}