順列を数え上げる系の問題、うまく「個数」を持った天才的な DP をするイメージ
問題概要
整数列 が与えられる。
この数列の 通りの順列のうち、 であるような がちょうど 個であるようなものの個数を 998244353 で割ったあまりを求めよ。
制約
考えたこと
安直には bitDP をしたくなる。こういう時って、天才的な DP をするイメージ。最初は箱根駅伝 DP かもと思ったけど、なんか違った。
とりあえず、 を小さい順に並べて、小さい順に挿入していくイメージを考えてみた。そしたらいきなり思いついた!
dp[i][j]
← 数列 のうち小さい順に 個を並べる方法のうち、 となっている の個数がちょうど 個であるようなものの個数
遷移の詳細を詰めるのは大変だけど、これで の計算量の解法になった。
コード
#include <bits/stdc++.h> using namespace std; #include <atcoder/modint> using mint = atcoder::modint998244353; int main() { int N, K; cin >> N >> K; vector<int> A(N); for (int i = 0; i < N; ++i) cin >> A[i]; sort(A.begin(), A.end()); vector<vector<mint>> dp(N+1, vector<mint>(N+1, 0)); dp[0][0] = 1; int m = 0; for (int i = 0; i < N; ++i) { if (i && A[i-1] < A[i]) m = i; for (int j = 0; j <= K; ++j) { dp[i+1][j] += dp[i][j] * (i+j+1-m); dp[i+1][j+1] += dp[i][j] * (m-j); } } cout << dp[N][K].val() << endl; }