1. 程式人生 > >Codeforces 285 E. Positions in Permutations

Codeforces 285 E. Positions in Permutations

== mod str 求長 更新 ram 長度 getch space


\(>Codeforces \space 285 E. Positions in Permutations<\)

題目大意 : 定義一個長度為 \(n\) 的排列中第 \(i\) 個元素是好的,當且僅當 \(i\)在排列中的位置 \(p_i\) 滿足 \(|i - p_i| = 1\), 給出 \(n, k\) 求長度為 \(n\) 的排列中恰好有 \(k\) 個元素是好的方案數

$1 \leq n \leq 1000, 0 \leq k \leq n $

解題思路 :

觀察發現,直接求出答案貌似十分困難,不妨先 \(dp\) 出至少 \(k\) 個元素是好的方案數,然後再通過 容斥/反演 來解決

至少有 \(k\) 個元素是好的方案數等價於有 \(k\) 個位置存在匹配 \(|i-p_i| = 1\) ,剩余元素隨便排列的方案數

\(f[i][j][0/1][0/1]\) 表示前 \(i\) 個元素和前 \(i\) 個位置產生了 \(j\) 對匹配,第 \(i\) 個元素和第 \(i\) 個位置是否已經被匹配的方案數

轉移很顯然,只需要枚舉 \(i-1\) 上的元素和位置是否能和 \(i\) 上的元素和位置產生匹配即可

\(F(k)\) 表示至少有 \(k\) 個是好的方案數,那麽 \(k = f[n][k] \times(n-k)!\)

考慮要求 \(Ans_k =\) 恰好有 \(k\)

個元素是好的方案數,那麽有 \(Ans_k = \sum_{i = k}^{n} (-1)^{i-k} \times C_i^k \times F(i)\)

這個和最基礎的容斥又有所不一樣,簡單的考慮,對於 \(i > k\) 的每一個 \(Ans_i\) ,其中任意選 \(k\) 個好元素都能更新到 \(Ans_k\) ,所以方案數是 \(C_i^k\) ,在容斥進行補集轉換的時候需要乘上這個系數

往復雜了講,這個涉及到容斥原理最本質的在集合上的運算,之前的 \(i\) 將表示所有大小為 \(i\) 的集合,那麽每一個大小為 \(i\) 的集合在算到 \(Ans_k\) 的過程中都產生了 \(C_i^k\)

個不同的大小為 \(k\) 的集合,當中的具體證明可以看近年集訓隊論文

考慮到前面算 \(F(i)\) 的時候,兩個不同的匹配方案可能會對應到同一個排列,這裏等價於每一個大小為 \(i\) 的集合,選 \(k\) 個好元素得到的新集合可能相同,所以兩個重復計數恰好抵消了



/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int f = 0, ch = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == ‘-‘) f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
#define N (2005)
#define int ll
const int Mod = 1000000007;
int js[N], inv[N], f[N][N][2][2], g[N], n, k;
inline int pow(int a, int b){
    int ans = 1;
    for(; b; b >>= 1, a = a * a % Mod)
        if(b & 1) ans = ans * a % Mod;
    return ans;
}
inline int C(int n, int i){ return js[n] * inv[i] % Mod * inv[n-i] % Mod; }
main(){
    read(n), read(k), f[0][0][0][0] = 1, js[0] = inv[0] = 1;
    for(int i = 1; i <= n; i++){
        js[i] = js[i-1] * i % Mod;
        inv[i] = pow(js[i], Mod - 2);
    }
    for(int i = 1; i <= n; i++)
        for(int j = 0; j <= n; j++){
            (f[i][j][0][0] += f[i-1][j][0][0] + f[i-1][j][1][1]) %= Mod;
            (f[i][j][0][0] += f[i-1][j][0][1] + f[i-1][j][1][0]) %= Mod;
            if(j >= 1 && i > 1){ 
                (f[i][j][1][0] += f[i-1][j-1][1][0] + f[i-1][j-1][0][0]) %= Mod;
                (f[i][j][0][1] += f[i-1][j-1][0][1] + f[i-1][j-1][0][0]) %= Mod;
            }
            if(j >= 2 && i > 1) (f[i][j][1][1] += f[i-1][j-2][0][0]) %= Mod;
        }
    for(int i = 0; i <= n; i++){
        int res = 0;
        (res += f[n][i][0][0] + f[n][i][1][0]) %= Mod;
        (res += f[n][i][0][1] + f[n][i][1][1]) %= Mod;
        g[i] = res * js[n-i] % Mod;
    }
    int ans = 0;
    for(int i = k; i <= n; i++){
        int p = ((i - k) & 1) ? Mod - 1 : 1; 
        (ans += C(i, k) * p % Mod * g[i] % Mod) %= Mod;
    }
    cout << ans << endl;
    return 0;
}

Codeforces 285 E. Positions in Permutations