Luogu P5505 [JSOI2011]分特产(二项式反演,DP)
文章目录
题目描述
有 $n$ 个人和 $m$ 种物品,第 $i$ 种物品有 $a_i$ 个,同种物品之间没有区别。
现在要将这些物品分给这些人,使得每个人至少分到一个物品,求方案数模 $10^9 + 7$
$n,m,a_i \le 10^3$
简要做法
每个人至少分到一个物品 $=$ 恰好 $0$ 个人没有分到物品
$f(i)$ 钦定,$g(i)$ 恰好
$$f(k) = \sum_{i=k}^{n}{i\choose k}g(i) = {n\choose k}\prod_{i=1}^{m}{{n-k+a_i-1}\choose{a_i-1}}$$
$$g(k)=\sum_{i=k}^{n}(-1)^{i-k}{i\choose k}f(i) = \sum_{i=k}^{n}(-1)^{i-k}{i\choose k}{n\choose i}\prod_{j=1}^{m}{{n-i+a_j-1}\choose{a_j-1}}$$
$$ans = g(0) = \sum_{i=0}^{n}(-1)^{i}{n\choose i}\prod_{j=1}^{m}{{n-i+a_j-1}\choose{a_j-1}}$$
参考代码
#include <stdio.h>
#include <algorithm>
#include <memory.h>
#define int long long
int read(int x = 0, int f = 0, char ch = getchar())
{
while ('0' > ch or ch > '9')
f = ch == '-', ch = getchar();
while ('0' <= ch and ch <= '9')
x = x * 10 + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
const int N = 2e3 + 5;
const int P = 1e9 + 7;
int n, m, ans;
int a[N];
int fac[N], inv[N], f[N], g[N];
int pow(int x, int k, int res = 1)
{
for (x %= P; k; x = x * x % P, k >>= 1)
if (k & 1)
res = res * x % P;
return res;
}
int C(int i, int j) { return fac[i] * inv[j] % P * inv[i - j] % P; }
signed main()
{
n = read(), m = read();
for (int i = 1; i <= m; i++)
a[i] = read();
fac[0] = 1;
for (int i = 1; i < N; i++)
fac[i] = fac[i - 1] * i % P;
inv[N - 1] = pow(fac[N - 1], P - 2);
for (int i = N - 1; i >= 1; i--)
inv[i - 1] = inv[i] * i % P;
for (int i = 0; i < n; i++)
{
int x = C(n, i);
for (int j = 1; j <= m; j++)
(x *= C(n - i + a[j] - 1, a[j])) %= P;
if (i & 1)
ans = (ans - x + P) % P;
else
ans = (ans + x) % P;
}
return printf("%lld", ans), 0;
}