#include #include #include #include using mint = atcoder::modint; // プレイヤー player の操作前を考える。 // stay グループにいるプレイヤーの中で最も順位の低いプレイヤーの順位の期待値 mint StayPlayer( const int player, const int stay, const int bottom, const int n) { if (player == n) return n - bottom; mint ans_0 = 0, ans_1 = 0; // どちらでもよいケースのためだけにメモしておく if (stay <= bottom) ans_0 = StayPlayer(player + 1, stay + 1, bottom, n) - 1; if (stay >= bottom) { ans_1 += StayPlayer(player + 1, stay, bottom, n); // 表 ans_1 += StayPlayer(player + 1, stay, bottom + 1, n); // 裏 ans_1 /= 2; } // プレイヤー player は操作を行った方が最適 if (stay < bottom) return ans_0; // プレイヤー player は操作を行わない方が最適 if (stay > bottom) return ans_1; return (ans_0 + ans_1) / 2; // どちらでもよい } // プレイヤー player の操作前を考える。 // bottom グループにいるプレイヤーの中で最も順位の低いプレイヤーの順位の期待値 mint BottomPlayer( const int player, const int stay, const int bottom, const int n) { if (player == n) return n; mint ans_0 = 0, ans_1 = 0; // どちらでもよいケースのためだけにメモしておく if (stay <= bottom) ans_0 = BottomPlayer(player + 1, stay + 1, bottom, n); if (stay >= bottom) { ans_1 += BottomPlayer(player + 1, stay, bottom, n); // 表 ans_1 += BottomPlayer(player + 1, stay, bottom + 1, n) - 1; // 裏 ans_1 /= 2; } // プレイヤー player は操作を行わない方が最適 if (stay < bottom) return ans_0; // プレイヤー player は操作を行った方が最適 if (stay > bottom) return ans_1; return (ans_0 + ans_1) / 2; // どちらでもよい } // プレイヤーの最適手は以下のみに依存することを前提としている。 // - 以前に操作を行わなかったプレイヤーの人数 // - 以前に操作を行って裏が出たプレイヤーの人数 int main() { constexpr int kMaxN = 3000, kMinP = 100000000, kMaxP = 1000000000; int n, p; std::cin >> n >> p; assert(1 <= n && n <= kMaxN); assert(kMinP <= p && p <= kMaxP); mint::set_mod(p); std::vector dp(1, std::vector{mint::raw(1)}); for (int player = 0; player < n; ++player) { mint ans = 0; std::vector nxt(player + 2, std::vector(player + 2, mint())); // 操作しなかったプレイヤー for (int stay = 0; stay <= player; ++stay) { // 下位が確定しているプレイヤー for (int behind = 0; stay + behind <= player; ++behind) { const mint prob = dp[stay][behind]; if (prob == 0) continue; mint rank_0 = 0, rank_1 = 0; if (stay <= behind) { rank_0 = StayPlayer(player + 1, stay + 1, behind, n); } if (stay >= behind) { rank_1 += player - (stay + behind) + 1; // 表 rank_1 += BottomPlayer(player + 1, stay, behind + 1, n); // 裏 rank_1 /= 2; } if (stay < behind) { ans += rank_0 * prob; nxt[stay + 1][behind] += prob; } else if (stay > behind) { ans += rank_1 * prob; nxt[stay][behind] += prob / 2; nxt[stay][behind + 1] += prob / 2; } else { assert(rank_0 == rank_1); ans += rank_0 * prob; // どちらでもよい nxt[stay + 1][behind] += prob / 2; nxt[stay][behind] += prob / 4; nxt[stay][behind + 1] += prob / 4; } } } dp.swap(nxt); std::cout << ans.val() << " \n"[player + 1 == n]; } return 0; }