結果

問題 No.93 ペガサス
ユーザー codershifthcodershifth
提出日時 2015-08-30 14:54:43
言語 C++11
(gcc 11.4.0)
結果
AC  
実行時間 505 ms / 5,000 ms
コード長 8,761 bytes
コンパイル時間 1,588 ms
コンパイル使用メモリ 163,212 KB
実行使用メモリ 152,132 KB
最終ジャッジ日時 2023-09-25 19:48:21
合計ジャッジ時間 5,220 ms
ジャッジサーバーID
(参考情報)
judge11 / judge12
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 2 ms
4,376 KB
testcase_01 AC 2 ms
4,376 KB
testcase_02 AC 2 ms
4,376 KB
testcase_03 AC 1 ms
4,376 KB
testcase_04 AC 79 ms
26,184 KB
testcase_05 AC 29 ms
11,436 KB
testcase_06 AC 19 ms
8,616 KB
testcase_07 AC 279 ms
84,892 KB
testcase_08 AC 130 ms
41,404 KB
testcase_09 AC 489 ms
148,648 KB
testcase_10 AC 5 ms
4,376 KB
testcase_11 AC 11 ms
6,472 KB
testcase_12 AC 410 ms
123,888 KB
testcase_13 AC 114 ms
36,584 KB
testcase_14 AC 42 ms
15,556 KB
testcase_15 AC 363 ms
109,944 KB
testcase_16 AC 56 ms
19,320 KB
testcase_17 AC 1 ms
4,380 KB
testcase_18 AC 2 ms
4,376 KB
testcase_19 AC 505 ms
152,132 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <bits/stdc++.h>

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<class T>                                                   \
    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<<m.value_;
        return os;
    }
};
// 互換性サポートは int, long long のみ
#define OPT(OP,T)                                                        \
    ModInt operator OP (const ModInt &a, T b) { return a OP ModInt(b); } \
    ModInt operator OP (T a, const ModInt &b) { return ModInt(a) OP b; }
    OPT(*,int) OPT(+,int) OPT(-,int) OPT(*,long long) OPT(+,long long) OPT(-,long long)
#undef  OPT

long long ModInt::Mod = (long long)(1E+9)+7;

namespace multi_dimentional_vector {
template<typename T> void resize(std::vector<T> &vec, size_t sz) { vec.resize(sz); }
template<typename T, typename S>
void resize(std::vector<T> &vec, size_t sz, const S &init) { vec.resize(sz, static_cast<T>(init)); }
template<typename T, typename... S>
void resize(std::vector<T> &vec, size_t sz, const S&... follows) {
        vec.resize(sz);
        for (auto &v : vec) resize(v, follows...);
}
template <typename T>
struct has_iterator {
    template <typename U> static char test(typename U::iterator* x);
    template <typename U> static long test(U* x);
    static const bool value = sizeof(test<T>(0)) == 1;
};
template <typename T, typename S>
typename std::enable_if<!has_iterator<T>::value, void>::type
fill(std::vector<T>& vec, const S &value) { std::fill(vec.begin(), vec.end(), static_cast<T>(value)); }

template <typename T, typename S>
typename std::enable_if<has_iterator<T>::value, void>::type
fill(std::vector<T>& vec, const S &value) { for (auto &v : vec) fill(v, value); }

template<typename T, typename... S>
std::vector<T> define(size_t sz, const S&... follows) {
        std::vector<T> 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<vector<vector<vector<ModInt>>>> 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<<dp[N][0][0][0]<<endl;
    }
};

#if 1
int main(int argc, char *argv[])
{
        ios::sync_with_stdio(false);
        auto obj = new Pegasus();
        obj->solve();
        delete obj;
        return 0;
}
#endif
0