给你两个长度为n n n 的序列a , b a, b a , b ,数之间两两不同
求把它们两两匹配后满足a i > b i a_i>b_i a i > b i 的对数比a i < b i a_i<b_i a i < b i 的对数恰好多k k k 对的方案数
n , k ≤ 2 0 0 0 n,k\le 2000 n , k ≤ 2 0 0 0
Links BZOJ3622
Luogu P4859
Solution 首先题目显然能够转化为求a i > b i a_i>b_i a i > b i 的对数恰好为k + n 2 \frac{k+n}{2} 2 k + n 的方案,方便起见,下文均用k k k 表示原来的k + n 2 \frac{k+n}{2} 2 k + n
发现恰好 并不好求,那么可以转化为至少 ,考虑如何求至少k k k 对的方案数
设d p [ i ] [ j ] dp[i][j] d p [ i ] [ j ] 表示处理到a a a 的第i i i 个,已经确定了j j j 对a i > b i a_i>b_i a i > b i 的方案数
将两个数组从小到大排序,设P o s [ i ] Pos[i] P o s [ i ] 表示b b b 中< a i <a_i < a i 的最靠后的数的下标,则有
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − 1 ] ∗ ( P o s [ i ] − j + 1 )
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1] * (Pos[i] - j + 1)
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − 1 ] ∗ ( P o s [ i ] − j + 1 ) 前一项是第i i i 个不匹配的方案数,后一项是第i i i 个匹配的方案数
这里需要解释一下后面的( P o s [ i ] − j + 1 ) (Pos[i] - j + 1) ( P o s [ i ] − j + 1 ) :
由于已经把数组排好序了,所以a a a 中的第i i i 个数能够在b b b 中匹配到的数,一定会包括< i <i < i 的能匹配的数,即它前面的能匹配的集合一定是它能匹配集合的一个子集
之前已经选出了j − 1 j-1 j − 1 个数,那么还剩P o s [ i ] − ( j − 1 ) Pos[i]-(j-1) P o s [ i ] − ( j − 1 ) 个数能选
这就是要把数组排序的原因,算是一个套路了吧
不难发现,d p [ n ] [ i ] dp[n][i] d p [ n ] [ i ] 乘上剩下n − i n-i n − i 个数随便放的方案数( n − i ) ! (n-i)! ( n − i ) ! 就是至少i i i 对的方案数
设f i f_i f i 为恰好i i i 对的方案数,g i g_i g i 为至少i i i 对的方案数,由二项式反演(也叫广义容斥原理)得:
g k = ∑ i = k n ( i k ) f i f k = ∑ i = k n ( − 1 ) i − k ( i k ) g i
\begin{aligned}
g_k&=\sum_{i=k}^{n}{i\choose k}f_i\\
f_k&=\sum_{i=k}^{n}(-1)^{i-k}{i\choose k}g_i
\end{aligned}
g k f k = i = k ∑ n ( k i ) f i = i = k ∑ n ( − 1 ) i − k ( k i ) g i
P.S. 这个形式的二项式反演并不是很会证,不过用容斥的思想感性理解记忆还是可以的
直接用g i = d p [ n ] [ i ] ∗ ( n − i ) ! g_i=dp[n][i]*(n-i)! g i = d p [ n ] [ i ] ∗ ( n − i ) ! 代入即可
Code 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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 #include <bits/stdc++.h> #define x first #define y second #define y1 Y1 #define y2 Y2 #define mp make_pair #define pb push_back using namespace std ;typedef long long LL;typedef pair<int , int > pii;template <typename T> inline int Chkmax (T &a, T b) { return a < b ? a = b, 1 : 0 ; }template <typename T> inline int Chkmin (T &a, T b) { return a > b ? a = b, 1 : 0 ; }inline void proc_status () { ifstream t ("/proc/self/status" ) ; cerr << string (istreambuf_iterator <char > (t), istreambuf_iterator <char > ()) <<endl ; } template <typename T> T read () { T sum = 0 , fl = 1 ; char ch = getchar(); for (; !isdigit (ch); ch = getchar()) if (ch == '-' ) fl = -1 ; for (; isdigit (ch); ch = getchar()) sum = (sum << 3 ) + (sum << 1 ) + ch - '0' ; return sum * fl; } const int Maxn = 2000 + 100 , Mod = 1e9 + 9 ;int N, K, A[Maxn], B[Maxn], Pos[Maxn], Dp[Maxn][Maxn];int fac[Maxn], ifac[Maxn];inline void Add (int &a, int b) { a += b; if (a >= Mod) a -= Mod; }inline int Pow (int a, int b) { int ans = 1 ; while (b) { if (b & 1 ) ans = 1l l * ans * a % Mod; a = 1l l * a * a % Mod; b >>= 1 ; } return ans; } inline int C (int n, int m) { return 1l l * fac[n] * ifac[m] % Mod * ifac[n - m] % Mod; }inline void Solve () { if ((N + K) & 1 ) { puts ("0" ); return ; } K = (N + K) >> 1 ; sort(A + 1 , A + N + 1 ), sort(B + 1 , B + N + 1 ); for (int i = 1 ; i <= N; ++i) Pos[i] = lower_bound(B + 1 , B + N + 1 , A[i]) - B - 1 ; 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] + 1l l * Dp[i - 1 ][j - 1 ] * max(0 , Pos[i] - j + 1 ) % Mod) % Mod; } int ans = 0 ; for (int i = K; i <= N; ++i) { if ((i - K) & 1 ) Add (ans, Mod - 1l l * C(i, K) * Dp[N][i] % Mod * fac[N - i] % Mod); else Add (ans, 1l l * C(i, K) * Dp[N][i] % Mod * fac[N - i] % Mod); } cout <<ans<<endl ; } inline void Input () { N = read<int >(), K = read<int >(); for (int i = 1 ; i <= N; ++i) A[i] = read<int >(); for (int i = 1 ; i <= N; ++i) B[i] = read<int >(); } inline void Init () { fac[0 ] = 1 ; for (int i = 1 ; i <= 2000 ; ++i) fac[i] = 1l l * fac[i - 1 ] * i % Mod; ifac[2000 ] = Pow (fac[2000 ], Mod - 2 ); for (int i = 2000 - 1 ; i >= 0 ; --i) ifac[i] = 1l l * ifac[i + 1 ] * (i + 1 ) % Mod; } int main () {#ifdef hk_cnyali freopen("A.in" , "r" , stdin ); freopen("A.out" , "w" , stdout ); #endif Input(); Init(); Solve(); return 0 ; }
Debug