BZOJ3622 已经没有什么好害怕的了 【dp + 二项式反演】
题目链接
题解
既已开题
那就已经没有什么好害怕的了
由题目中奇怪的条件我们可以特判掉\(n - k\)为奇数时答案为\(0\)
否则我们要求的就是糖果大于药片恰好有\(\frac{n - k}{2} + k\)个的方案数,我们记为\(K\)
思路1
直接求恰好不好求,想到二项式反演:
如果有
\[b_k = \sum\limits_{i = k}^{n} {i \choose k} a_i\]
那么有
\[a_k = \sum\limits_{i = k}^{n} (-1)^{i - k} {i \choose k} b_i\]
如果我们令\(a_k\)为恰好有\(K\)对糖果大于药片的方案数
从式子来看,对于每一种\(a_i\)的情况,\(b_k\)都从中选出任意\(k\)个
那么\(b_k\)就是任选\(k\)个满足条件,剩余随便选的方案数
问题就转化为了求出\(b_k\),显然可以\(dp\)
我们先将糖果与药片分别升序排序
我们设\(f[i][j]\)表示前\(i\)个糖果选出\(j\)个和药片匹配,都大于药片的方案数
我们记\(pos_i\)为第\(i\)个糖果比药片大的最大的位置,那么如果第\(i\)个糖果参与匹配,就有\(pos_i\)种选择
由于糖果是升序排序,所以剩余\(j - 1\)个糖果选择范围一定不比\(i\)大,所以\(i\)实际剩余\(pos_i - (j - 1)\)种选择
那么有
\[f[i][j] = f[i - 1][j] + f[i - 1][j - 1] * (pos_i - j + 1)\]
求出\(f
[i]\),则\(b_k = f
[k] * (n - k)!\)
然后再用二项式反演即可求出\(a_k\)
思路2
如果你不知道二项式反演,其实有一个更直观的方法
同样是求出\(f
[i]\)后求出\(b_i\)
我们令\(ans[k]\)表示恰好\(k\)个糖果大于药片的方案数
显然
\[ans
= b_n\]
当\(k < n\)时,\(b_k\)多算了恰好为\(k + 1\)、\(k + 2\)、\(k + 3......\)的方案数,减去即可
\[ans[k] = b_k - \sum\limits_{i = k + 1}^{n} ans[i]\]
这样也是对的
PS
由思路2其实我们可以看到二项式反演实际上就是一个容斥的结果
思路一
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<map> #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) #define REP(i,n) for (int i = 1; i <= (n); i++) #define mp(a,b) make_pair<int,int>(a,b) #define cls(s) memset(s,0,sizeof(s)) #define cp pair<int,int> #define LL long long int using namespace std; const int maxn = 2005,maxm = 100005,INF = 1000000000,P = 1000000009; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();} while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } LL f[maxn][maxn],C[maxn][maxn],fac[maxn]; int a[maxn],b[maxn]; int n,K; void init(){ fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % P; for (int i = 0; i <= n; i++){ C[i][0] = C[i][i] = 1; for (int j = 1; j <= (i >> 1); j++) C[i][j] = C[i][i - j] = (C[i - 1][j - 1] + C[i - 1][j]) % P; } sort(a + 1,a + 1 + n); sort(b + 1,b + 1 + n); } int main(){ n = read(); K = read(); if ((n - K) & 1) {puts("0"); return 0;} K = ((n - K) >> 1) + K; for (int i = 1; i <= n; i++) a[i] = read(); for (int i = 1; i <= n; i++) b[i] = read(); init(); f[0][0] = 1; for (int i = 1; i <= n; i++){ f[i][0] = f[i - 1][0]; int pos = n; while (pos && b[pos] >= a[i]) pos--; for (int j = 1; j <= i; j++) f[i][j] = (f[i - 1][j] + f[i - 1][j - 1] * max(pos - j + 1,0) % P) % P; } LL ans = 0,t; for (int i = K; i <= n; i++){ t = ((i - K) & 1) ? -1 : 1; ans = ((ans + t * C[i][K] % P * (f [i] * fac[n - i] % P) % P) % P + P) % P; } printf("%lld\n",ans); return 0; }
思路2
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<map> #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) #define REP(i,n) for (int i = 1; i <= (n); i++) #define mp(a,b) make_pair<int,int>(a,b) #define cls(s) memset(s,0,sizeof(s)) #define cp pair<int,int> #define LL long long int using namespace std; const int maxn = 2005,maxm = 100005,INF = 1000000000,P = 1000000009; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();} while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } LL f[maxn][maxn],C[maxn][maxn],fac[maxn],ans[maxn]; int a[maxn],b[maxn]; int n,K; void init(){ fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % P; for (int i = 0; i <= n; i++){ C[i][0] = C[i][i] = 1; for (int j = 1; j <= (i >> 1); j++) C[i][j] = C[i][i - j] = (C[i - 1][j - 1] + C[i - 1][j]) % P; } sort(a + 1,a + 1 + n); sort(b + 1,b + 1 + n); } int main(){ n = read(); K = read(); if ((n - K) & 1) {puts("0"); return 0;} K = ((n - K) >> 1) + K; for (int i = 1; i <= n; i++) a[i] = read(); for (int i = 1; i <= n; i++) b[i] = read(); init(); f[0][0] = 1; for (int i = 1; i <= n; i++){ f[i][0] = f[i - 1][0]; int pos = n; while (pos && b[pos] >= a[i]) pos--; for (int j = 1; j <= i; j++) f[i][j] = (f[i - 1][j] + f[i - 1][j - 1] * max(pos - j + 1,0) % P) % P; } ans = f ; for (int i = n - 1; i >= K; i--){ ans[i] = f [i] * fac[n - i] % P; for (int j = i + 1; j <= n; j++) ans[i] = (ans[i] - C[j][i] * ans[j] % P) % P; ans[i] = (ans[i] + P) % P; } printf("%lld\n",ans[K]); return 0; }
- [BZOJ3622]已经没有什么好害怕的了 二项式反演
- 【BZOJ3622】已经没有什么好害怕的了(DP,二项式反演)
- bzoj3622 已经没有什么好害怕的了(容斥原理+DP+二项式反演)
- 【BZOJ3622】已经没有什么好害怕的了 容斥+DP
- [BZOJ 3622]已经没有什么好害怕的了
- bzoj3622-已经没有什么好害怕的的了
- bzoj 3622: 已经没有什么好害怕的了 dp+容斥
- BZOJ3622: 已经没有什么好害怕的了
- 【BZOJ3622】已经没有什么好害怕的了,DP+容斥原理
- BZOJ 3622 已经没有什么好害怕的了
- [bzoj3622]已经没有什么好害怕的了
- [BZOJ3622]已经没有什么好害怕的了-容斥原理-二项式反演
- ●BZOJ 3622 已经没有什么好害怕的了
- BZOJ 3622 已经没有什么好害怕的了
- bzoj 3622 已经没有什么好害怕的了
- BZOJ 3622: 已经没有什么好害怕的了 [容斥原理 DP]
- 【BZOJ3622】已经没有什么好害怕的了 动态规划+容斥原理
- 【BZOJ3622】已经没有什么好害怕的了
- [bzoj3622]已经没有什么好害怕的了
- BZOJ3622: 已经没有什么好害怕的了 解题报告