牛客练习赛71 C 数学考试
数学考试
https://ac.nowcoder.com/acm/contest/7745/C
我们发现可以通过第一个不合法的位置不重不漏的统计所有非法方案。
设dp[i]表示处理到sa[i],且sa[1],sa[2]...sa[i - 1]这些点都是合法的方案数。
那么答案就是n! - dp[1] * (n - sa[1])! - dp[2] * (n - sa[2])! - ... - dp[m] * (n - sa[m])!。
考虑怎么求这个dp[i]。我们发现处理到sa[i]时,有sa[i]!种不同的不合法方案,但是有些方案会在前面的点不合法,减掉即可。
只要dp方程设计的足够优秀,代码就能足够简单。
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; typedef long long ll; const int N = 2e3 + 100; const int MOD = 20000311; ll fac[N]; int n, m; int sa[N]; ll dp[N]; ll solve() { sort(sa + 1, sa + m + 1); dp[1] = fac[sa[1]]; for (int i = 2; i <= m; i++) { dp[i] = fac[sa[i]]; for (int j = 1; j < i; j++) { dp[i] = (dp[i] - dp[j] * fac[sa[i] - sa[j]]) % MOD; } } ll ans = fac[n]; for (int i = 1; i <= m; i++) ans = (ans - dp[i] * fac[n - sa[i]]) % MOD; ans = (ans % MOD + MOD) % MOD; return ans; } int main() { //freopen("0.txt", "r", stdin); fac[0] = 1; for (int i = 1; i < N; i++) fac[i] = fac[i - 1] * i % MOD; scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) scanf("%d", sa + i); printf("%lld\n", solve()); return 0; }