做题CF285E. Positions in Permutations——dp+容斥
Posted cly-none
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了做题CF285E. Positions in Permutations——dp+容斥相关的知识,希望对你有一定的参考价值。
题意:求所有长度为(n)的排列(p)中,有多少个满足:对于所有(i ,(1 leq i leq n)),其中恰好有(k)个满足(|p_i - i| = 1)。答案对(10^9 + 7)取模。
(n leq 10^3)
首先,让我们考虑这个类似反演的结论:
对于(F(n))和(f(n)),则满足
[F(n) = sum_{k geq n}{{k}choose{n}}f(k) iff f(n) = sum_{k geq n}(-1)^{k-n}{{k}choose{n}}F(k)]
对于充分性,我们有
[
egin{aligned} & sum_{k geq n}(-1)^{k-n}{{k}choose{n}}F(k) \
= & sum_{k geq n}(-1)^{k-n}{{k}choose{n}} sum_{j geq k} {{j}choose{k}} f(j) = & sum_{j geq n} f(j) {{j}choose{n}} sum_{n leq k leq j} (-1)^{k-n} {{j-n}choose{k-n}} = & sum_{j geq n} f(j) {{j}choose{n}} sum_{0 leq k leq j-n} (-1)^k {{j-n}choose{k}} = & sum_{j geq n} f(j) {{j}choose{n}} (1-1)^{j-n} = & f(n)end{aligned}
]
而对于必要性,我们也能给出类似的证明。
观察这个结论,不难发现,我们平时使用的容斥就是这个结论求(f(0))时的特殊情况。而现在我们要求的是(f(k)),问题就变成了把所有(F(n))都求出来。
(F(n))的定义正对应了我们求至少有(k)个的情况总数时,重复统计所得到的结果。这使得它可以比较容易地求出。
我们考虑权值和位置表示为二分图的形式,那么问题就在于求二分图恰好有(k)个匹配的方案数。考虑dp。设dp[i,j,a,b]表示当前匹配到第(i)个权值和位置,已经有(j)个匹配,并且(a)和(b)分别表示第(i)个位置与权值是否已经与编号更小的权值与位置匹配。通过枚举(a)和(b),很容易能得到dp的转移。当然,最后的答案还要乘以一个阶乘。
时间复杂度(O(n^2))。
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, MOD = 1000000007;
typedef long long ll;
ll power(ll a,int b) {
ll res = 1;
while (b) {
if (b&1) res = 1ll * res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
int n,k;
ll dp[N][N][2][2],jc[N],inv[N],ans;
ll comb(int a,int b) {
if (a < 0 || b < 0 || a < b)
return 0;
return jc[a] * inv[b] % MOD * inv[a-b] % MOD;
}
int main() {
scanf("%d%d",&n,&k);
dp[0][0][1][1] = 1;
for (int i = 1 ; i <= n ; ++ i)
for (int j = 0 ; j <= n ; ++ j) {
(dp[i][j][0][0] = dp[i-1][j][0][0] + dp[i-1][j][1][0] + dp[i-1][j][0][1] + dp[i-1][j][1][1]) %= MOD;
if (j >= 1) {
(dp[i][j][1][0] = dp[i-1][j-1][0][0] + dp[i-1][j-1][1][0]) %= MOD;
(dp[i][j][0][1] = dp[i-1][j-1][0][0] + dp[i-1][j-1][0][1]) %= MOD;
}
if (j >= 2)
dp[i][j][1][1] = dp[i-1][j-2][0][0];
}
jc[0] = 1;
for (int i = 1 ; i <= n ; ++ i)
jc[i] = 1ll * i * jc[i-1] % MOD;
inv[n] = power(jc[n],MOD-2);
for (int i = n-1 ; i >= 0 ; -- i)
inv[i] = 1ll * inv[i+1] * (i+1) % MOD;
for (int j = k, p = 1 ; j <= n ; ++ j, p = -p) {
ll v = 1ll * jc[n-j] * (dp[n][j][0][0] + dp[n][j][1][0] + dp[n][j][0][1] + dp[n][j][1][1]) % MOD;
(ans += p * comb(j,k) * v % MOD) %= MOD;
}
ans = (ans % MOD + MOD) % MOD;
cout << ans << endl;
return 0;
}
小结:这种类似于反演的东西是很多组合问题的通用方法,希望自己能实现灵活的运用。
以上是关于做题CF285E. Positions in Permutations——dp+容斥的主要内容,如果未能解决你的问题,请参考以下文章
CF285E Positions in Permutations(dp+容斥)
Codeforces 285 E. Positions in Permutations
CodeForces - 285E: Positions in Permutations(DP+组合数+容斥)
题解CF#175(Div. 2) E-Positions in Permutations