[BZOJ 3622]已經沒有什麽好害怕的了
阿新 • • 發佈:2018-06-29
href line time CA tps oid pla ret times
Description
題庫鏈接
給出 \(n\) 個數 \(a_i\) ,以及 \(n\) 個數 \(b_i\) ,要求兩兩配對使得 \(a>b\) 的對數減去 \(a<b\) 的對數等於 \(k\) 。
\(0\leq k\leq n\leq 2000\) ,保證 \(a,b\) 無相同元素。
Solution
我們假設 \(a>b\) 對數為 \(x\) ,可以求得 \(x=\frac{n+k}{2}\) 。
我們令 \(f_{i,j}\) 表示前 \(i\) 個 \(a\) 中,選了 \(j\) 組滿足 \(a>b\) 的方案數。
容易得到 \(\text{dp}\) 方程
\[f_{i,j}=f_{i-1,j}+(l_i-j+1)\times f_{i-1,j-1}\]
其中 \(l_i\) 表示從小到大排序後 \(b\) 中 \(<a_i\) 的最靠後一個數。
我們記 \(g_i=f_{n,i}\times (n-i)!\) 即滿足 \(a>b\) 的組數 \(\geq i\) 的方案數,再令 \(f_i\) 表示恰好滿足 \(a>b\) 的組數 \(= i\) 的方案數。
容易發現對於 \(i>j\) \(f_i\) 恰好在 \(g_j\) 中算了 {i\choose j} 次。
那麽存在
\[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)\]
直接求解即可。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2000+5, yzh = 1e9+9;
int n, k, a[N], b[N], l[N], f[N][N], fac[N], ifac[N], g[N];
int C(int n, int m) {return 1ll*fac[n]*ifac[m]%yzh*ifac[n-m]%yzh; }
void work() {
scanf(" %d%d", &n, &k);
if ((n+k)&1) {puts("0"); return; } k = (n+k)/2;
ifac[0] = ifac[1] = fac[0] = fac[1] = 1;
for (int i = 2; i < N; i++) ifac[i] = -1ll*yzh/i*ifac[yzh%i]%yzh;
for (int i = 2; i < N; i++)
fac[i] = 1ll*fac[i-1]*i%yzh, ifac[i] = 1ll*ifac[i-1]*ifac[i]%yzh;
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+n+1); sort(b+1, b+n+1);
int loc = 0;
for (int i = 1; i <= n; i++) {
while (loc < n && b[loc+1] < a[i]) ++loc;
l[i] = loc;
}
f[0][0] = 1;
for (int i = 1; i <= n; i++) {
f[i][0] = f[i-1][0];
for (int j = 1; j <= i; j++)
f[i][j] = (1ll*f[i-1][j]+1ll*f[i-1][j-1]*max(0, l[i]-j+1)%yzh)%yzh;
}
for (int i = 0; i <= n; i++) g[i] = 1ll*f[n][i]*fac[n-i]%yzh;
int ans = 0;
for (int i = k; i <= n; i++)
if ((i-k)&1) (ans -= 1ll*C(i, k)*g[i]%yzh) %= yzh;
else (ans += 1ll*C(i, k)*g[i]%yzh) %= yzh;
printf("%d\n", (ans+yzh)%yzh);
}
int main() {work(); return 0; }
[BZOJ 3622]已經沒有什麽好害怕的了