P4859 已经没有什么好害怕的了

题目链接

最近在啃二项式反演,这道题就是一道很好的综合题,从0到最后完成,整个人的收获是非常多的。

首先整体思路没什么好说的:

  • \(a\)至少比\(b\)\(k\)对,也就是说:

1、\(a\)大于\(b\)的应该有(n + k) >> 1个
2、\((n + k) >> 1\)为偶数

  • 先排序,定义一个\(cnt\)数组,\(cnt[i]\)数组代表,在\(i\)位置及以前,存在多少个\(b\)\(a_i\)小。
  • DP:求前\(n\)个人中恰好有\(i\)对组合,\(dp\)状态转移方程先根据实际意义推一下,\(dp[i][j]\)代表前\(a_i\)个恰好有\(j\)对的方案数有多少:我们可以推出来,\(dp[i][j]\)可以从\(dp[i - 1][j]\)转移过来,因为在\(i-1\)的位置已经满足了\(j\)对,所以在\(i\)位置就不做任何操作,另外,还可以从\(dp[i-1][j-1]\)的位置转移过来,在\(i-1\)位置是\(j-1\)对,在\(i\)位置如果存在\(cnt[i]-j+1\)对的话,那么\(dp[i-1][j-1]\)就可以为\(dp[i][j]\)贡献\(dp[i-1][j-1] \cdot (cnt[i]-j+1)\)多对(要减掉(\(j-1\))的)。
  • 根据上面的\(dp[n][i]\)可以得到至少有多少对,也就是前面那个式子,敲好有\(i\)对的话,也就是前\(i\)个一定满足,后\(n-i\)个一定不满足,根据这里,得到至少有\(g[n]\)对,且\(g[k]=dp[n][k]*fac[n-k]\)
  • 然后再定义一个变量\(ans\),它的实际含义就是最终答案,根据二项式反演可以得出

\[ans = \sum_{i=k}^n \cdot (-1)^{i-k} \cdot C_i^k \cdot g[i] \]

到这里问题已经结束了
接下来就是代码
写这个博客目的并不是为了把整个题的思路都写下来,只是写一些让我觉得不错的题,或者说中间一些容易错的思路。

#include <bits/stdc++.h>
using namespace std;

#define mp make_pair
#define pb push_back
typedef long long ll;
const int N = 2e3 + 7, Mod = 1e9 + 9;
int a[N], b[N], n, k, cnt[N] = {0};
ll dp[N][N], f[N], frac[N], ret = 0, inv[N];

ll ksm(ll a, ll p){
    ll ans = 1;
    while (p){
        if (p & 1)  ans = (ans * a) % Mod;
        a = (a * a) % Mod;
        p >>= 1;
    }
    return ans;
}

void pre(){
    int loc = 0, sz = 2e3;
    for (int i = 1; i <= n; i++) {
        while (loc < n && b[loc+1] < a[i]) ++loc;
        cnt[i] = loc;
    }
    frac[1] = inv[1] = 1;
    for (int i = 2; i <= sz; i++)    frac[i] = (frac[i - 1] * i) % Mod;
    for (int i = 1; i <= sz; i++)    inv[i] = ksm(i, Mod - 2) % Mod;
}

ll C(int n, int m){
    ll ans = 1;
    for (int i = n, sz = n - m; i > sz; i--)    ans = ans * i % Mod;
    for (int i = 2; i <= m; i++)    ans = ans * inv[i] % Mod;
    return ans;
}

void solve(){
    cin >> n >> k;
    if ((n + k) & 1){
        cout << "0" << endl;
        return;
    }
    k = (n + k) >> 1;
    for (int i = 1; i <= n; i++)    cin >> a[i];
    for (int i = 1; i <= n; i++)    cin >> b[i];
    sort(a + 1, a + 1 + n);
    sort(b + 1, b + 1 + n);
    pre();
    dp[0][0] = 1;
    for (int i = 1; i <= n; i++){
        dp[i][0] = 1;
        for (int j = 1; j <= cnt[i]; j++)
            dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1] * max(cnt[i] - j + 1, 0) % Mod) % Mod;
    }
    for (int i = 1; i <= n; i++)    f[i] = dp[n][i] * frac[n - i] % Mod;
    for (int i = k, flg = 1; i <= n; i++, flg = -flg)
        ret = (ret + flg * C(i, k) * f[i] % Mod) % Mod;
    cout << (ret % Mod + Mod) % Mod << endl;
}

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t--) solve();
    return 0;
}

posted @ 2021-03-10 17:47  Liberals  阅读(33)  评论(0)    收藏  举报