【题目链接】
【dyllalala的题解】
说下我个人的理解...
先把两个数组排序,设next[i]表示满足第i个糖果>第j个药片的最大的j。显然这个next是单调的,可以O(n)求出。
设dp[i][j]表示前i个糖果里,至少有j组糖果>药片(即这j组以外的其他情况都不考虑)的方案数。考虑第i个糖果,有两种转移:
(1)不管,dp[i][j] = dp[i - 1][j]
(2)组成一组关系,dp[i][j] = dp[i - 1][j - 1] * (next[i] - j + 1)
然后我们就求出了dp[i][j]。
现在考虑重复的情况:我们要从“至少”求出“恰好”。
设f[j]表示n个糖果里,恰好有j组糖果>药片(显然其他组都是药片>糖果)的方案数。那么有
f[j] = dp[n][j] * (n - j)! - ∑(f[i] * C(i, j)),j + 1 <= i <= n
前一项是后面n - j组的总方案,后一项是分别把“至少”多出来的每一项减去。
圆神的三个题终于做完了
/* Pigonometry */ #include <cstdio> #include <algorithm> using namespace std; typedef long long LL; const int maxn = 2005, p = 1000000009; int n, k, A[maxn], B[maxn], next[maxn]; LL C[maxn][maxn], fact[maxn], dp[maxn][maxn]; inline int iread() { int f = 1, x = 0; char ch = getchar(); for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1; for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0'; return f * x; } int main() { n = iread(); k = iread(); if((n - k) & 1) { printf("0\n"); return 0; } k = (n + k) >> 1; fact[0] = 1; for(int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i % p; for(int i = 0; i <= n; i++) { C[i][0] = 1; for(int j = 1; j <= i; j++) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % p; } for(int i = 1; i <= n; i++) A[i] = iread(); for(int i = 1; i <= n; i++) B[i] = iread(); sort(A + 1, A + 1 + n); sort(B + 1, B + 1 + n); for(int i = 1, j = 0; i <= n; next[i++] = j) for(; j < n && A[i] > B[j + 1]; j++); dp[0][0] = 1; for(int i = 1; i <= n; i++) { dp[i][0] = 1; for(int j = 1; j <= i; j++) dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1] * (next[i] - j + 1) % p) % p; } for(int i = n; i >= k; i--) { dp[n][i] = dp[n][i] * fact[n - i] % p; for(int j = i + 1; j <= n; j++) dp[n][i] = (dp[n][i] - dp[n][j] * C[j][i] % p + p) % p; } printf("%lld\n", dp[n][k]); return 0; }