#include typedef long long ll; typedef unsigned long long ull; #define FOR(i,a,b) for(int (i)=(a);i<(b);i++) #define REP(i,n) FOR(i,0,n) #define RANGE(vec) (vec).begin(),(vec).end() class ModInt { long long value_; public: static long long Mod; static void set_mod(long long mod) { Mod= mod; } static long long get_mod(void) { return Mod; } ModInt() : value_(0) {} ModInt(long long val) { if (val >= Mod) value_ = val % Mod; else if (val >= 0) value_ = val; else // val < 0 value_ = (((-val)/Mod+1)*Mod+val) % Mod; assert(value_ < Mod); } ModInt(const ModInt &other) { value_ = other.value_; } ~ModInt() {} #define OPT(OP) \ ModInt operator OP (const ModInt &other) const { \ return ModInt(value_ OP other.value_); \ } \ template \ ModInt &operator OP##=(const T &other) { \ return (*this = (*this OP ModInt(other))); \ } OPT(+) OPT(-) OPT(*) #undef OPT bool operator==(const ModInt &other) const { return (value_ == other.value_); } bool operator!=(const ModInt &other) const { return !(*this == other); } ModInt &operator=(const ModInt &other) { value_ = other.value_; return *this; } // cast overload operator int() const { return value_; } operator long long() const { return value_; } static long long pow(ModInt a, int k) { ModInt b = 1; while (k > 0) { if (k & 1) b = (b*a); a = (a*a); k >>= 1; } return b; } // call when Mod is prime ModInt inv() { return ModInt::pow(*this, Mod-2); } long long value() const { return value_; } friend std::ostream& operator<<(std::ostream& os, const ModInt& m) { os< void resize(std::vector &vec, size_t sz) { vec.resize(sz); } template void resize(std::vector &vec, size_t sz, const S &init) { vec.resize(sz, static_cast(init)); } template void resize(std::vector &vec, size_t sz, const S&... follows) { vec.resize(sz); for (auto &v : vec) resize(v, follows...); } template struct has_iterator { template static char test(typename U::iterator* x); template static long test(U* x); static const bool value = sizeof(test(0)) == 1; }; template typename std::enable_if::value, void>::type fill(std::vector& vec, const S &value) { std::fill(vec.begin(), vec.end(), static_cast(value)); } template typename std::enable_if::value, void>::type fill(std::vector& vec, const S &value) { for (auto &v : vec) fill(v, value); } template std::vector define(size_t sz, const S&... follows) { std::vector vec; return std::move(vec); } }; namespace mdv = multi_dimentional_vector; using namespace std; class Pegasus { public: void solve(void) { // // 縦横にはおけないのと N*N 盤面であることから // // +-+-+-+-+ // | |o| | | // +-+-+-+-+ // |o| | | | // +-+-+-+-+ // | | | |o| // +-+-+-+-+ // | | |o| | // +-------+ // [2,1,4,3] // // のように 1...n の順列のうち隣り合う数字との差の絶対値が // 2 にならないようなものの総数を求めれば良い。 // 以下隣接する数字の差の絶対値が 2 になる条件を cond と表す。 // // 挿入 DP でやる。 // 1,2,3,..,N の順に挿入して順列をつくると考える。 // // 1,2,..,n の順列にたいして n+1 を挿入する時 // * n-1 の隣に挿入してしまうと cond をみたすものが 1 つできる。 // (1,2,... の順で挿入するので片側だけ考えれば良い。) // * ただし n-3,n-1 が隣接するとき間に cond を満たすものの総数は変わらない ((-1) + (+1) = 0) // * それ以外の cond をみたす隣接2数の間に挿入すれば cond を満たすものの総数を減らせる。 // // n+1 の挿入時に n-3,n-1 が隣接しているかの情報が必要になる。 // つまり次の更新時のために n,n-2 が隣り合うかの情報更新が必要。 // // 以上を踏まえると dp は // dp[n][k][a][b] := n 番目まで挿入したときに、 cond をみたす場所が k 個あるときの挿入結果の総数 // ただし a==1 のとき (n-3,n-1) が隣接 // ただし b==1 のとき (n-2,n) が隣接 // // となる。 int N; cin>>N; vector>>> dp; mdv::resize(dp, N+1, N+1, 2, 2, 0); dp[1][0][0][0] = 1; // O(N^2) FOR(n,1,N) REP(k,N) REP(a,2) REP(b,2) { if (n == 1) { // 1 枚しかないときはどこに挿入しても cond を満たさない dp[n+1][k][0][0] += (n+1)*dp[n][k][a][b]; continue; } // (n-2,n) -> (n-3,n-1) // (n-1,n+1) -> (n-2,n) if (a==1) // (n-3,n-1) が隣接 { // n-1 のとなりに挿入するケース // (n-3,n-1) の間に挿入 dp[n+1][k][b][1] += dp[n][k][a][b]; // (n-3,n-1)じゃないほうの n-1 のとなり dp[n+1][k+1][b][1] += dp[n][k][a][b]; // cond 総数を減らすような挿入。 k-1 なのは上の (n-3,n-1) のケースを省くから if (k >= 1) { if (b==1) // (n-2,n) が隣接するケース { // (n-2,n) の間に挿入 dp[n+1][k-1][0][0] += dp[n][k][a][b]; // それ以外 dp[n+1][k-1][1][0] += ((k-1)-1)*dp[n][k][a][b]; } else dp[n+1][k-1][b][0] += (k-1)*dp[n][k][a][b]; } // のこり (n+1) なのは 1,...,n の両側を含めるから dp[n+1][k][b][0] += ((n+1)-k-1)*dp[n][k][a][b]; } else { // n-1 のとなりに挿入(左右二通り) dp[n+1][k+1][b][1] += 2*dp[n][k][a][b]; // k を減らすような挿入 if (k >= 1) { if (b==1) { // (n-2,n) の間に挿入 dp[n+1][k-1][0][0] += dp[n][k][a][b]; // それ以外 dp[n+1][k-1][1][0] += (k-1)*dp[n][k][a][b]; } else dp[n+1][k-1][b][0] += k*dp[n][k][a][b]; } // のこり dp[n+1][k][b][0] += ((n+1)-k-2)*dp[n][k][a][b]; } } cout<solve(); delete obj; return 0; } #endif