「COCI2019-2020#5」 Zapina 题解
「COCI2019-2020#5」 Zapina 题解
题意
有 \(n\) 个人和 \(n\) 道题,现在把每个题给人,至少有一个下标为 \(i\) 的人得到 \(i\) 道题, 问分配的方案数量
解
考虑用所有的方案数减去不开心的方案数。
所有的方案数就是 \(n^n\)
不开心的方案数用 DP 解决, 用 \(f_{i,j}\) 表示前 \(i\) 个人 \(j\) 道题目不开心的数量。
用三重循环做这个题,即 \(i, j, k\)
表示分配到第 \(i\) 个人,前 \(j\) 道题里面给了这个人 \(k\) 道题。
先假设题目都是一样的, 就有这样的状态转移方程
\[f_{i, j} = f_{i, j} + f_{i - 1, j - k}
\]
第二部分表示上一步的状态,即分配到了 \(i-1\)个人,已经分配了 \(j-k\) 道题的状态。
在这里题目都是不一样的,所以这个选择题目要乘上一个\(C(j,k)\), 表示在 \(j\) 里面选择 \(k\) 个题。
Code
#include <bits/stdc++.h>
using i64 = long long;
constexpr i64 N = 400;
constexpr i64 md = 1e9 + 7;
inline i64 q_pow(i64 a, i64 b) {
i64 ans = 1;
while (b) {
if (b & 1) ans = (ans * a) % md;
a = (a * a) % md;
b >>= 1;
}
return ans % md;
}
i64 f[N + 1][N + 1];
i64 dp[N + 1][N + 1];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);std::cout.tie(0);
i64 n;
std::cin >> n;
f[0][0] = f[1][0] = f[1][1] = 1;
for (i64 i = 2; i <= n; i++) {
f[i][0] = 1;
}
for (i64 i = 2; i <= n; i++) {
for (i64 j = 1; j <= n; j++) {
f[i][j] = (f[i - 1][j - 1] + f[i - 1][j]) % md;
}
}
dp[0][0] = 1;
for (i64 i = 1; i <= n; i++) {
for (i64 j = 0; j <= n; j++) {
for (i64 k = 0; k <= j; k++) {
if (k != i)
dp[i][j] = (dp[i][j] + dp[i - 1][j - k] * f[j][k]) % md;
}
}
}
i64 ans = (q_pow(n, n) % md - dp[n][n] + md) % md;
std::cout << ans % md << std::endl;
return 0;
}