BZOJ3622(容斥+dp)

思路

  • “恰k个”考虑求至少k、k+1……个容斥
  • 题面说所有数字都不同,可以将所求转化为糖比药多的组数恰为\((n+k)/2\)的方案数
  • \(f[i][j]\)数组我觉得更好的理解方式是"前i个已经安排了j组糖大于药、别的先没管"的方案数
  • \(f[n][i]*(n-i)!\)即为把其它的安排了以后的方案数,但是这里面有重的
  • \(g[i]\)为恰i个的方案数。\[g[i]=f[n][i]*(n-i)!-\sum_{j=i+1}^ng[j]*C_j^i\]要说为什么又去重又剪掉不合法了,我也不通透,目前只是已知这样做是对的话那直观感受一下应该对吧……
#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long ll;
const int mod = 1e9 + 9;
const int maxn = 2005;

int n, k, m, ans;
int a[maxn], b[maxn];
ll f[maxn][maxn], g[maxn];
ll fac[maxn], C[maxn][maxn];

int READ() {
    scanf("%d %d", &n, &k);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++)
        scanf("%d", &b[i]);
    m = (n + k) / 2;
    return (n + k) % 2;
}

void PRE() {
    sort(a + 1, a + 1 + n);
    sort(b + 1, b + 1 + n);

    fac[0] = 1;
    for (int i = 1; i <= n; i++)
        fac[i] = fac[i - 1] * i % mod;

    for (int i = 0; i <= n; i++) {
        C[i][0] = 1;
        for (int j = 1; j <= i; j++)
            C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
    }
}

void DP() {
    for (int i = 0; i <= n; i++)
        f[i][0] = 1;
    for (int i = 1, p = 0; i <= n; i++) {
        for (; p < n && b[p + 1] < a[i]; p++);
        for (int j = 1; j <= i; j++) {
            f[i][j] = (f[i - 1][j - 1] * max(0, p - j + 1) % mod + f[i - 1][j]) % mod;
        }
    }
    for (int i = n; i >= m; i--) {
        g[i] = f[n][i] * fac[n - i] % mod;
        for (int j = i + 1; j <= n; j++) {
            g[i] = (g[i] - g[j] * C[j][i] % mod) % mod;
        }
    }
    ans = (g[m] + mod) % mod;
}

int main() {    
    if (READ() == 1) {
        return !printf("0\n");
    }
    PRE();
    DP();
    return !printf("%d\n", ans);
}

转载于:https://www.cnblogs.com/AlphaWA/p/10934739.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值