F - Permutation and Minimum Editorial by potato167


任意の整数 \(1\leq i\leq N\) に対して、\(\min(A_{2i+ 1}, A_{2i + 2}) = -1\) が成り立つとします。

小さい値から順番に \(A\) に当てはめます。

\(dp[i][j][k]\) を \(i\) まで入れて、を以下の条件を満たす数列の数とします。

  • 値はもともと \(A\) に入ってなかった \(i\) 以下の値で、大きい方の値がもともと \(A\) に入っていて、 \(i\) より大きいものの個数がちょうど\(j\) 個

  • 値は \(i\) 以下の値で、大きい方の値がもともと \(A\) に入っていなくて、\(i\) より大きいものの個数がちょうど \(k\) 個

どのように更新するかを考えます。

\(i\) がもともと \(A\) に入っているとき

\(i\) を小さい値にするとき、\(k\) が \(1\) 増えます。

\(i\) を大きい値にするとき、\(j\) が \(1\) 減ります。また、どの値にするかで \(j\) 通りあります。

\(i\) がもともと \(A\) に入っていないとき

\(i\) を小さい値にするとき、 大きい値をもともと \(A\) に入っている値にするなら、 \(j\) が \(1\) 増え、そうでないなら \(k\) が \(1\) 増えます。

\(i\) を大きい値にするとき、 \(k\) が \(1\) 減ります。

この考えをもとに、\(dp[2N][0][0]\) を求めます。 そして、その値に \(\min(A_{2i+ 1}, A_{2i + 2}) = -1\) を満たす \(i\) の個数の階乗をかけたものが答えです。

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i=(int)(a);i<(int)(b);i++)


#include <atcoder/modint>
using mint = atcoder::modint1000000007;


int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int N;
    cin >> N;
    vector<int> A(N * 2);
    rep(i, 0, N * 2){
        cin >> A[i];
        A[i]--;
    }
    int X = 0;
    vector<int> seen(N * 2);
    rep(i, 0, N){
        if (A[i * 2] > A[i * 2 + 1]) swap(A[i * 2], A[i * 2 + 1]);
        if (A[i * 2] != -2){
            seen[A[i * 2]] = 2;
            seen[A[i * 2 + 1]] = 2;
        }
        else if (A[i * 2 + 1] == -2){
            X++;
        }
        else{
            seen[A[i * 2 + 1]] = 1;
        }
    }
    vector dp(N + 1, vector<mint>(N + 1));
    dp[0][0] = 1;
    rep(i, 0, N * 2){
        if (seen[i] == 2) continue;
        vector n_dp(N + 1, vector<mint>(N + 1));
        if (seen[i] == 1){
            rep(j, 0, N + 1) rep(k, 0, N + 1){
                if (j) n_dp[j - 1][k] += dp[j][k] * j;
                if (k != N) n_dp[j][k + 1] += dp[j][k];
            }
        }
        else{
            rep(j, 0, N + 1) rep(k, 0, N + 1){
                if (j != N) n_dp[j + 1][k] += dp[j][k];
                if (k != N) n_dp[j][k + 1] += dp[j][k];
                if (k) n_dp[j][k - 1] += dp[j][k];
            }
        }
        swap(n_dp, dp);
    }
    rep(i, 0, X) dp[0][0] *= i + 1;
    cout << dp[0][0].val() << "\n";
}

posted:
last update: