CF285E Positions in Permutations

直接计数是很不好记的,但 “恰好” 这两个字眼太有警示性了,我们可以考虑使用二项式反演来解决这个问题。

回忆二项式反演的流程,我们先考虑钦定一些位置合法其他位置随意的方案数。然后我们惊奇地发现这样一个事实,假如我们钦定某个位置 \(i\) 满足 \(|P_i - i| = 1\),显然 \(P_i = i + 1, i - 1\) 那么这个位置能否选择 \(i + 1\) 是与之前钦定的位置无关的而其他没有被钦定的位置是还未确定的因此也无关,只有能否选择 \(i - 1\)\(i - 2\) 这个位置有没有选 \(i - 2 + 1\) 有关,因此我们在考虑计数的时候,必须要知道 \(i - 2\) 这个位置的状态,而为了后面的转移能继承 \(i - 2\) 这个位置的状态,我们必须把当前位置选择的状态记录在状态当中,可以发现这个位置选 \(i - 1\) 或者不钦定对后面点的影响都是一样的,于是我们可以简单地定义这样一个 \(dp\),令 \(dp_{i, j, 0 / 1, 0 / 1}\) 表示当前考虑完前 \(i\) 个位置,有 \(j\) 个位置被钦定为满足条件,当前位置是选 \(i - 1\) 或不钦定还是选 \(i + 1\)\(i - 1\) 是选 \(i - 1\) 或不钦定还是选 \(i\),那么就会有如下转移(通过讨论当前位置选 \(i - 1\),不钦定,选 \(i + 1\) 来确定转移):

\[\begin{aligned} dp_{i, j, 0, 0} &= dp_{i - 1, j, 0, 0} + dp_{i - 1, j, 0, 1} + dp_{i - 1, j - 1, 0, 0}\\ dp_{i, j, 0, 1} &= dp_{i - 1, j, 1, 0} + dp_{i - 1, j, 1, 1} + dp_{i - 1, j - 1, 1, 0}\\ dp_{i, j, 1, 0} &= dp_{i - 1, j - 1, 0, 0} + dp_{i - 1, j - 1, 0, 1}\\ dp_{i, j, 1, 1} &= dp_{i - 1, j - 1, 1, 0} + dp_{i - 1, j - 1, 1, 1} \end{aligned} \]

则根据二项式反演,令 \(f_i\) 为钦定有 \(i\) 个位置满足条件其余位置随意的方案,则:

\[f_i = (dp_{n, i, 0, 0} + dp_{n, i, 0, 1}) \times (n - i)! \]

\(g_i\) 表示恰好 \(i\) 个位置满足条件的方案,则有 \(f, g\) 的关系:

\[f_i = \sum\limits_{j = i} ^ n \dbinom{j}{i} g_j \]

根据二项式反演:

\[g_k = \sum\limits_{i = k} ^ n (-1) ^ {i - k} \dbinom{i}{k} (dp_{n, i, 0, 0} + dp_{n, i, 0, 1}) \times (n - i)! \]

直接计算即可。

#include<bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for(int i = l; i <= r; ++i)
const int N = 1000 + 5;
const int Mod = 1000000000 + 7;
int n, m, ans, f[N], fac[N], C[N][N], dp[N][N][2][2];
int read(){
    char c; int x = 0, f = 1;
    c = getchar();
    while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int Inc(int a, int b){
    return (a += b) >= Mod ? a - Mod : a;
}
int Dec(int a, int b){
    return (a -= b) < 0 ? a + Mod : a;
}
int Mul(int a, int b){
    return 1ll * a * b % Mod;
}
int main(){
    n = read(), m = read();
    fac[0] = C[0][0] = 1;
    rep(i, 1, n) C[i][0] = 1, fac[i] = Mul(fac[i - 1], i);
    rep(i, 1, n) rep(j, 1, i) C[i][j] = Inc(C[i - 1][j - 1], C[i - 1][j]);
    dp[1][0][0][0] = dp[1][1][1][0] = 1;
    rep(i, 2, n) rep(j, 0, i){
        dp[i][j][0][0] = Inc(dp[i - 1][j][0][0], dp[i - 1][j][0][1]);
        if(j) dp[i][j][0][0] = Inc(dp[i][j][0][0], dp[i - 1][j - 1][0][0]);
        dp[i][j][0][1] = Inc(dp[i - 1][j][1][0], dp[i - 1][j][1][1]);
        if(j) dp[i][j][0][1] = Inc(dp[i][j][0][1], dp[i - 1][j - 1][1][0]);
        if(j) dp[i][j][1][0] = Inc(dp[i - 1][j - 1][0][0], dp[i - 1][j - 1][0][1]);
        if(j) dp[i][j][1][1] = Inc(dp[i - 1][j - 1][1][0], dp[i - 1][j - 1][1][1]);
    }
    rep(i, 0, n) f[i] = Mul(Inc(dp[n][i][0][0], dp[n][i][0][1]), fac[n - i]);
    rep(i, m, n){
        if((i + m) & 1) ans = Dec(ans, Mul(C[i][m], f[i]));
        else ans = Inc(ans, Mul(C[i][m], f[i]));
    }
    printf("%d", ans);
    return 0;
}
posted @ 2020-09-02 20:19  Achtoria  阅读(135)  评论(0编辑  收藏  举报