BZOJ3622 已经没有什么好害怕的了 二项式反演+DP

题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=3622

题解

首先显然如果 \(n - k\) 为奇数那么就是无解。否则的话,“糖果”比“药片”大的组数,应该为 \(\frac {n+k}2\)

考虑到多恰好 \(k\) 组不太好求,但是如果选了 \(k\) 组必须是“糖果”比“药片”大,这个方案数还是很好求的。

首先是选了 \(k\) 组必须是“糖果”比“药片”大的方案数,这可以用一个 dp 解决。我们将糖果排序,然后预处理 \(s_i\) 表示有 \(s_i\) 个药片比糖果小。设 \(dp[i][j]\) 表示前 \(i\) 个糖果选了 \(j\) 个糖果比药片大的组合。转移的时候枚举当前这一个选不选就可以了。

\[dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1] \cdot(s_i - j + 1) \]

然后我们令 \(f(k)\) 表示钦定了 \(i\) 对“糖果”比“药片”大以后的方案数。那么显然有

\[f(k)=dp[n][k]\cdot(n-k)! \]

同时可以发现,如果令 \(g(k)\) 为恰好 \(i\) 对“糖果”比“药片”大的方案数,那么有

\[f(k)=\sum_{i=k}^n \binom ik g(i) \]

所以直接二项式反演就可以了。


代码如下,时间复杂度 \(O(n^2)\)

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I>
inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 2000 + 7;
const int P = 1e9 + 9;

int n, k;
int a[N], b[N], s[N], dp[N][N], f[N];

inline int smod(int x) { return x >= P ? x - P : x; }
inline void sadd(int &x, const int &y) { x += y; x >= P ? x -= P : x; }
inline int fpow(int x, int y) {
	int ans = 1;
	for (; y; y >>= 1, x = (ll)x * x % P) if (y & 1) ans = (ll)ans * x % P;
	return ans;
}

int fac[N], inv[N], ifac[N];
inline void ycl(const int &n = ::n) {
	fac[0] = 1; for (int i = 1; i <= n; ++i) fac[i] = (ll)fac[i - 1] * i % P;
	inv[1] = 1; for (int i = 2; i <= n; ++i) inv[i] = (ll)(P - P / i) * inv[P % i] % P;
	ifac[0] = 1; for (int i = 1; i <= n; ++i) ifac[i] = (ll)ifac[i - 1] * inv[i] % P;
}
inline int C(int x, int y) {
	if (x < y) return 0;
	return (ll)fac[x] * ifac[y] % P * ifac[x - y] % P;
}

inline void work() {
	if ((n - k) & 1) {
		puts("-1");
		return;
	} else k = (n + k) / 2;
	
	std::sort(a + 1, a + n + 1);
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j) if (b[j] < a[i]) ++s[i];
	dp[0][0] = 1;
	for (int i = 1; i <= n; ++i)
		for (int j = 0; j <= i; ++j) {
			dp[i][j] = dp[i - 1][j];
			if (j) sadd(dp[i][j], (ll)dp[i - 1][j - 1] * (s[i] - j + 1) % P);
		}
		
	ycl();
	for (int i = 0; i <= n; ++i) f[i] = (ll)dp[n][i] * fac[n - i] % P;
	int ans = 0;
	for (int i = k; i <= n; ++i)
		if ((i - k) & 1) sadd(ans, P - (ll)C(i, k) * f[i] % P);
		else sadd(ans, (ll)C(i, k) * f[i] % P);
	printf("%d\n", ans);
}

inline void init() {
	read(n), read(k);
	for (int i = 1; i <= n; ++i) read(a[i]);
	for (int i = 1; i <= n; ++i) read(b[i]);
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	init();
	work();
	fclose(stdin), fclose(stdout);
	return 0;
}
posted @ 2019-09-11 08:13  hankeke303  阅读(144)  评论(0编辑  收藏  举报