您的位置:首页 > 其它

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

2016-04-23 08:39 405 查看
【题目链接】

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

printf("%lld\n", dp
[k]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: