【组合数学】专题笔记目录
考虑 DP,设 dpi 表示最多填充 i 种颜色的方案数。
这里做一个区分:题目中要求的是恰好 C 种颜色。
那就可以表示出答案:ans = \sum_{i=0}^{C} (-1)^{C - i} \binom{C}{i} \times dp_i。
- 现在进行解释:由于 dp_i 是最多的方案数,所以 dp_i 包含 dp_{1 \dots i-1},因此要容斥,系数就是 (-1)^{C - i}。
- 由于总共有 C 种颜色,并且颜色之间没有区分(即可以任意选 i 种颜色)所以要乘上系数 \binom{C}{i},即 C 种选i 种。
- 最后肯定要乘上方案数 dp_i。
那如何求出 dp_i 呢?考虑递推转移。
依旧可以容斥:dp_i = \sum_{j=0}^{m} (-1)^{m-j} \times \binom{m}{j} \times ((i + 1)^j - 1)^n
- 容斥就不多说了,显然这也是有“互相包含”的情况所以要容斥。
- 总共有 m 列,每一列之间没有区分,所以 \binom{m}{i}。
- ((i + 1)^j - 1)^n 是因为每一个位置可以放 i + 1 种(i 种颜色和 1 个不放颜色)由于每一行有 j 个被选中,每一行就有 (i + 1)^j 种方法。但是由于不能全空,所以要 -1,最后因为每一行都满足以上性质,所以要再 n 次幂。
#include <bits/stdc++.h>
using namespace std;
const int N = 415;
const int mod = 1e9 + 7;
int n, m, C;
int qmi(int a, int k) {
int res = 1;
while (k) {
if (k & 1) res = (res * 1ll * a) % mod;
a = (a * 1ll * a) % mod;
k >>= 1;
}
return res;
}
int fac[N], inv[N];
void init() {
fac[0] = 1;
for (int i = 1; i <= 400; i++) fac[i] = (fac[i - 1] * 1ll * i) % mod;
inv[400] = qmi(fac[400], mod - 2);
for (int i = 399; i >= 0; i--) inv[i] = (inv[i + 1] * 1ll * (i + 1)) % mod;
}
int c(int n, int m) {
return ((fac[n] * 1ll * inv[m]) % mod * 1ll * inv[n - m]) % mod;
}
int dp[N];
int main() {
scanf("%d%d%d", &n, &m, &C);
init();
for (int i = 1; i <= C; i++) {
int x = 1;
for (int j = m; j >= 1; j--) {
int res = (x * c(m, j) * 1ll * qmi(qmi(i + 1, j) - 1, n)) % mod;
dp[i] = (dp[i] * 1ll + res * 1ll + mod) % mod;
x *= -1;
}
}
// for (int i = 0; i <= C; i++) cout << dp[i] << ' '; puts("");
long long ans = 0;
int x = 1;
for (int i = C; i >= 0; i--) {
int res = (x * c(C, i) * 1ll * dp[i]) % mod;
ans = (ans * 1ll + res * 1ll) % mod;
x *= -1;
}
ans = (ans + mod) % mod;
printf("%lld\n", ans);
return 0;
}