【BZOJ3622】已经没有什么好害怕的了【计数DP】【姿势】

【题目链接】

【dyllalala的题解】

说下我个人的理解...

先把两个数组排序,设next[i]表示满足第i个糖果>第j个药片的最大的j。显然这个next是单调的,可以O(n)求出。

设dp[i][j]表示前i个糖果里,至少有j组糖果>药片(即这j组以外的其他情况都不考虑)的方案数。考虑第i个糖果,有两种转移:

(1)不管,dp[i][j] = dp[i - 1][j]

(2)组成一组关系,dp[i][j] = dp[i - 1][j - 1] * (next[i] - j + 1)

然后我们就求出了dp[i][j]。

现在考虑重复的情况:我们要从“至少”求出“恰好”。

设f[j]表示n个糖果里,恰好有j组糖果>药片(显然其他组都是药片>糖果)的方案数。那么有

f[j] = dp[n][j] * (n - j)! - ∑(f[i] * C(i, j)),j + 1 <= i <= n

前一项是后面n - j组的总方案,后一项是分别把“至少”多出来的每一项减去。


圆神的三个题终于做完了

/* Pigonometry */
#include <cstdio>
#include <algorithm>

using namespace std;

typedef long long LL;

const int maxn = 2005, p = 1000000009;

int n, k, A[maxn], B[maxn], next[maxn];
LL C[maxn][maxn], fact[maxn], dp[maxn][maxn];

inline int iread() {
	int f = 1, x = 0; char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return f * x;
}

int main() {
	n = iread(); k = iread();
	if((n - k) & 1) {
		printf("0\n");
		return 0;
	}
	k = (n + k) >> 1;

	fact[0] = 1;
	for(int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i % p;
	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]) % p;
	}

	for(int i = 1; i <= n; i++) A[i] = iread();
	for(int i = 1; i <= n; i++) B[i] = iread();
	sort(A + 1, A + 1 + n); sort(B + 1, B + 1 + n);

	for(int i = 1, j = 0; i <= n; next[i++] = j)
		for(; j < n && A[i] > B[j + 1]; j++);

	dp[0][0] = 1;
	for(int i = 1; i <= n; i++) {
		dp[i][0] = 1;
		for(int j = 1; j <= i; j++)
			dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1] * (next[i] - j + 1) % p) % p;
	}

	for(int i = n; i >= k; i--) {
		dp[n][i] = dp[n][i] * fact[n - i] % p;
		for(int j = i + 1; j <= n; j++)
			dp[n][i] = (dp[n][i] - dp[n][j] * C[j][i] % p + p) % p;
	}

	printf("%lld\n", dp[n][k]);
	return 0;
}


你可能感兴趣的:(【BZOJ3622】已经没有什么好害怕的了【计数DP】【姿势】)