题意:
给定两个序列$a$和$b$,让它们进行匹配,求出使得$a_i > b_j$的个数比$a_i < b_j$的个数恰好多$k$,求这样的匹配方法数
题解:
这题的各种表示有一点相似又截然不同,很容易混淆。
直接求恰好满足$k$对不好求,所以先放宽条件,这样子有利于构造动规方程。
先用$f_{i, j}$表示在前$i$个中,至少选择$j$个$a > b$的匹配的方案数(是匹配的方案数,只关心匹配那一部分,不关心其它的部分),容易得到动规方程:
ƒi,j = ƒi - 1,j + (Lasti - (j - 1)) * ƒi - 1,j - 1
其中$Last_i$表示第一个小于$a_i$的$b_j$。
$(Last_i - (j - 1))$表示原有$Last_i$种选择,被选走了$j - 1$种,此时因为是“至少”,所以其它的匹配是不用管的。
那么现在考虑求出恰好为$k$的方案数。
首先令$g_i$表示前$N$个$a$中,满足至少有$i$个$a > b$的方案数,那么
gi = ƒN,j * (N - i) !
这时候才考虑了其它的部分,所以需要乘上阶乘。
再令$f'_i$表示恰好满足$i$组的方案数,那么考虑容斥,在所有的$g_j$中,每个$f'_i (i > j)$被算了$C_i^j$次,因为不考虑其它的,仅$i$个已匹配好的任意取$j$个,其它的随便排,正好被$g_j$囊括,当然这一部分是多余的,所以
ƒ'i = gj - Cj, i * ƒ'j (j > i)
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 6 #define MOD 1000000009 7 8 using namespace std; 9 10 typedef long long LL; 11 12 const int MAXN = 2000 + 10; 13 14 LL g[MAXN][MAXN]; 15 16 LL f[MAXN]= {0}; 17 18 LL fac[MAXN]; 19 LL C[MAXN][MAXN]; 20 21 int N, K; 22 23 int Candy[MAXN], Pill[MAXN]; 24 25 int Last[MAXN]= {0}; 26 27 void Preparation () { 28 fac[0] = 1; 29 for (int i = 1; i <= N; i ++) 30 fac[i] = fac[i - 1] * i % MOD; 31 32 for (int i = 0; i <= N; i ++) 33 C[i][0] = 1; 34 for (int i = 1; i <= N; i ++) 35 for (int j = 1; j <= i; j ++) 36 C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD; 37 } 38 39 int main () { 40 scanf ("%d%d", & N, & K); 41 42 Preparation (); 43 44 for (int i = 1; i <= N; i ++) 45 scanf ("%d", & Candy[i]); 46 for (int i = 1; i <= N; i ++) 47 scanf ("%d", & Pill[i]); 48 49 sort (Candy + 1, Candy + N + 1); 50 sort (Pill + 1, Pill + N + 1); 51 52 for (int i = 1; i <= N; i ++) 53 for (int j = N; j >= 1; j --) 54 if (Pill[j] < Candy[i]) { 55 Last[i] = j; 56 break; 57 } 58 59 for (int i = 0; i <= N; i ++) 60 g[i][0] = 1; 61 for (int i = 1; i <= N; i ++) 62 for (int j = 1; j <= i; j ++) 63 g[i][j] = (g[i - 1][j] + (Last[i] - j + 1) * g[i - 1][j - 1] % MOD) % MOD; 64 65 for (int i = N; i >= 1; i --) { 66 f[i] = g[N][i] * fac[N - i] % MOD; 67 for (int j = i + 1; j <= N; j ++) 68 f[i] = ((f[i] - C[j][i] * f[j] % MOD) % MOD + MOD) % MOD; 69 } 70 71 printf ("%lld ", f[(N + K) >> 1]); 72 73 return 0; 74 } 75 76 /* 77 4 2 78 5 35 15 45 79 40 20 10 30 80 */