洛谷·bzoj·已经没有什么好害怕的了

初见安~这里是传送门:洛谷P4859 & bzoj P3622

题解

这就是个广义容斥定理的板子题了。说白了就是假设有n个物品,求刚好k个满足某个性质时的算法。【不做解释了。

这个题要求糖果能量>药片的情况数要大于反之的情况数刚好k组。也就是说:

假设有x组糖果>药片,y组反之,则:

\left\{\begin{matrix} x+y=n \\ x-y=k \end{matrix}\right. 可得:x = \frac{n+k}{2}

所以首先n+k为奇数就无解。其次,我们要求的就是刚好有\frac{n+k}{2}组满足糖果>药片时的方案数了。

由广义容斥,我们首先要求得至少有i组满足该性质时的数量。这个我们首先可以用计数dp解决——f][i][j]表示前i颗糖果,有j组都是能量大于药片的情况数。状态很好转移——

f[i][j] = f[i-1][j]+f[i-1][j-1]*max(0,cnt[i]-j+1);

即:要么该糖不做贡献,要么整个能量小于它的药片,而剩余的可以小于它的药片数有cnt[i]-(j-1)个。

所以就有:\alpha(k) = f[n][k]*(n-k)!,即至少满足k个性质的数量,因为剩下n-k个随便搭配了。

然后套广义容斥即可。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 2005
using namespace std;
typedef long long ll;
const int mod = 1e9 + 9;
const int mx = 2e3 + 2;
int read() {
    int x = 0, f = 1, ch = getchar();
    while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
    while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
    return x * f;
}
 
ll fac[maxn], inv[maxn];
ll pw(ll a, ll b) {ll res = 1; while(b) {if(b & 1) res = res * a % mod; a = a * a % mod, b >>= 1;} return res;}
ll C(ll n, ll m) {return fac[n] * inv[m] % mod * inv[n - m] % mod;}
 
ll ans = 0, f[maxn][maxn], alpha[maxn];
int n, k, cd[maxn], md[maxn], cnt[maxn];
signed main() {
    fac[0] = 1, inv[0] = 1;//预处理逆元
    for(int i = 1; i <= mx; i++) fac[i] = fac[i - 1] * i % mod;
    inv[mx] = pw(fac[mx], mod - 2);
    for(int i = mx - 1; i > 0; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
     
    n = read(), k = read();
    for(int i = 1; i <= n; i++) cd[i] = read();//candy
    for(int i = 1; i <= n; i++) md[i] = read();//medicine
    sort(cd + 1, cd + 1 + n); sort(md + 1, md + 1 + n);
    for(int i = 1, p = 1; i <= n; i++) {
        while(p <= n && cd[i] > md[p]) p++;
        cnt[i] = p - 1;//记录第i种糖果大于多少种药片
    }
     
    for(int i = 0; i <= n; i++) f[i][0] = 1;
    for(int i = 1; i <= n; i++) for(int j = 1; j <= i; j++) 
        f[i][j] = (f[i - 1][j] + f[i - 1][j - 1] * max(0, cnt[i] - j + 1)) % mod;
    k = (n + k) >> 1;//所求要满足的性质数
    for(int i = 1; i <= n; i++) alpha[i] = f[n][i] * fac[n - i] % mod;//这i组满足后,其余随便放
    for(int i = k, kd = 1; i <= n; i++, kd = -kd) 
        ans = (ans + kd * alpha[i] * C(i, k) + mod) % mod;//广义容斥啊,可理解为i个里固定k个满足要求
    printf("%lld\n", ans);
    return 0;
}

迎评:)
——End——

发布了158 篇原创文章 · 获赞 23 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43326267/article/details/103665248