听说是套路我不会的容斥+DP题.
Description
给定长度为 $N$ 的数组 $a[],b[]$.
要把它们配对起来,使得恰好有 $K$ 对满足 $a[i]>b[ P[i] ]$,求方案数.
$N \le 2000$.
Solution
首先, 列个方程是可以求出到底要多少对满足条件的配对.
那么接下来就考虑怎么求出恰好$k$对. 我们先放宽条件, 求至少$k$对的方案, 最后用一个二项式容斥系数即可. 证明见CQZhangYu的博客.
下面是DP. 对于一个状态$f_{i,j}$, 糖果做到$i$, 有至少$k$对满足条件. 将两个数组排序, 我们可以预处理出对于糖果$i$最大的位置$p$, 满足$a_i > b_p$. 那么有转移:
$f[i][j] = f[i- 1][j] + f[i-1][j-1] \times (p - j + 1)$.
我们在最后一个系数中可以减去被重复计数的部分.
???
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include <bits/stdc++.h> #define mod 1000000009
using namespace std; typedef long long ll;
const int maxn = 2005;
int n, k, a[maxn], b[maxn], p[maxn]; ll c[maxn][maxn], f[maxn][maxn], fac[maxn];
void init() { c[0][0] = 1; for (int i = 1; i <= n; ++i) { c[i][0] = 1; for (int j = 1; j <= i; ++j) c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod; } fac[0] = 1; for (int i = 1; i <= n; ++i) fac[i] = (fac[i - 1] * i) % mod; }
int main() { scanf("%d%d", &n, &k); if ((n ^ k) & 1) { puts("0"); return 0; } k = ((n + k) >> 1); init(); for (int i = 1; i <= n; ++i) scanf("%d", &a[i]); for (int i = 1; i <= n; ++i) scanf("%d", &b[i]); sort(a + 1, a + 1 + n); sort(b + 1, b + 1 + n); f[0][0] = 1; for (int i = 1; i <= n; ++i) { int k = 1; for (; k <= n && b[k] < a[i]; ++k); --k; for (int j = 1; j <= i; ++j) f[i][j] = (f[i - 1][j] + f[i - 1][j - 1] * max(k - j + 1, 0) % mod) % mod; f[i][0] = f[i - 1][0]; } ll coe = 1, ans = 0; for (int i = k; i <= n; ++i, coe = -coe) f[n][i] = f[n][i] * fac[n - i] % mod, ans = (ans + coe * f[n][i] * c[i][k] % mod + mod) % mod; printf("%lld\n", (ans + mod) % mod); return 0; }
|