1. 程式人生 > >[BZOJ 3622] 已經沒有什麽好害怕的了 手動反演

[BZOJ 3622] 已經沒有什麽好害怕的了 手動反演

什麽 fin col lin name freopen 一次 二項式 online

題意

  給定兩個大小為 n 的集合 A = {a[1], a[2], ..., a[n]} , B = {b[1], b[2], ..., b[n]} , 元素兩兩不同.

  定義 L(A) 為 A 生成的排列的集合.

  給定 K , 求 $\sum_{X \in L(A), Y \in L(B)} [\sum_{k = 1} ^ n [X_k > Y_k] - \sum_{k = 1} ^ n [X_k < Y_k] = K]$ .

  1 <= n <= 2000, 0 <= K <= n .

分析

  令 $K = \frac{n + K}{2}$ , 求恰好存在 K 個 X[i] > Y[i] 的排列有多少對.

  設 f[i] 表示欽定有 i 個位, X[i] > Y[i] , 並且不考慮其他位.

  f[i] 可以通過 DP 求得.

  F[i][j] = F[i-1][j] + F[i-1][j-1] * (cnt[i] - (j-1)) .

  不能直接用 f[K] * (n-K)! + f[K+1] * (n-(K+1))! - ...

  因為從第二項開始算的次數都不是一次, 而是一個謎之二項式系數的乘積的和.

  設 g[i] 表示恰好有 i 個位, X[i] > Y[i] .

  g[n] = f[n] .

  $g[i] = f[i] - \sum_{j = i+1} ^ n g[j] \binom{j}{i}$ .

  這裏的做法, 與 "手動的莫比烏斯反演" 是一個原理.

小結

  對於這類恰好有 K 個位的問題, 我們可以考慮手動反演.

  G[K] 表示恰好有 K 個位, F[K] 表示欽定有 K 個位.

  G[n] = F[n] .

  G[i] = F[i] - ∑ G[j] * 迷之系數.

實現

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cctype>
 5 #include <algorithm>
 6 using
namespace std; 7 #define F(i, a, b) for (register int i = (a); i <= (b); i++) 8 #define P(i, a, b) for (register int i = (a); i >= (b); i--) 9 inline int rd(void) { 10 int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == -) f = -1; 11 int x = 0; for (; isdigit(c); c = getchar()) x = x*10+c-0; return x*f; 12 } 13 14 const int N = 2005; 15 const int MOD = (int)1e9 + 9; 16 17 int n, K, C[N][N], fac[N], A[N], B[N], cnt[N]; 18 int f[N], g[N]; 19 20 int main(void) { 21 #ifndef ONLINE_JUDGE 22 freopen("bzoj3622.in", "r", stdin); 23 #endif 24 25 n = rd(), K = rd(); 26 if ((K + n) & 1) return puts("0"), 0; 27 K = (K + n) >> 1; 28 29 F(i, 0, n) { 30 C[i][0] = 1; 31 F(j, 1, i) 32 C[i][j] = (C[i-1][j] + C[i-1][j-1]) % MOD; 33 } 34 fac[0] = 1; 35 F(i, 1, n) fac[i] = 1LL * fac[i-1] * i % MOD; 36 37 F(i, 1, n) A[i] = rd(); F(i, 1, n) B[i] = rd(); 38 sort(A+1, A+n+1), sort(B+1, B+n+1); 39 for (int cur = 1, _cur = 0; cur <= n; cur++) { 40 while (_cur+1 <= n && B[_cur+1] < A[cur]) 41 _cur++; 42 cnt[cur] = _cur; 43 } 44 45 f[0] = 1; 46 F(i, 1, n) 47 P(j, i, 1) 48 f[j] = (f[j] + 1LL * f[j-1] * (cnt[i] - (j-1))) % MOD; 49 50 g[n] = f[n]; 51 P(i, n-1, K) { 52 g[i] = 1LL * f[i] * fac[n-i] % MOD; 53 F(j, i+1, n) 54 g[i] = (g[i] - 1LL * g[j] * C[j][i]) % MOD; 55 } 56 printf("%d\n", (g[K] + MOD) % MOD); 57 58 return 0; 59 }

[BZOJ 3622] 已經沒有什麽好害怕的了 手動反演