[BZOJ 3622]已经没有什么好害怕的了

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; }
posted @ 2018-06-29 19:43  NaVi_Awson  阅读(577)  评论(0编辑  收藏  举报