初见安~这里是传送门:洛谷P4859 & bzoj P3622
题解
这就是个广义容斥定理的板子题了。说白了就是假设有n个物品,求刚好k个满足某个性质时的算法。【不做解释了。
这个题要求糖果能量>药片的情况数要大于反之的情况数刚好k组。也就是说:
假设有x组糖果>药片,y组反之,则:
可得:
所以首先n+k为奇数就无解。其次,我们要求的就是刚好有组满足糖果>药片时的方案数了。
由广义容斥,我们首先要求得至少有i组满足该性质时的数量。这个我们首先可以用计数dp解决——表示前i颗糖果,有j组都是能量大于药片的情况数。状态很好转移——
即:要么该糖不做贡献,要么整个能量小于它的药片,而剩余的可以小于它的药片数有cnt[i]-(j-1)个。
所以就有:,即至少满足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——