#include #include #include #include #include #include #include using mint = atcoder::modint; std::int64_t MyHash(const int x, const int y, const int z, const int n) { return (std::int64_t{x} * n + y) * n + z; } // プレイヤー player の操作前を考える。 // stay グループにいるプレイヤーの中で最も順位の低いプレイヤーの順位の期待値 mint StayPlayer( const int player, const int stay, const int bottom, const int n) { if (player == n) return n - bottom; static std::unordered_map memo; const std::uint64_t key = MyHash(player, stay, bottom, n); if (const auto it = memo.find(key); it != memo.end()) return it->second; 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 memo[key] = ans_0; // プレイヤー player は操作を行わない方が最適 if (stay > bottom) return memo[key] = ans_1; return memo[key] = (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; static std::unordered_map memo; if (player == n + 1) { std::cout << memo.size() << '\n'; exit(0); } const std::uint64_t key = MyHash(player, stay, bottom, n); if (const auto it = memo.find(key); it != memo.end()) return it->second; 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 memo[key] = ans_0; // プレイヤー player は操作を行った方が最適 if (stay > bottom) return memo[key] = ans_1; return memo[key] = (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::unordered_map dp{{0, mint::raw(1)}}; for (int player = 0; player < n; ++player) { mint ans = 0; std::unordered_map nxt; for (const auto& [key, prob] : dp) { if (prob == 0) continue; const int stay = key / n, behind = key % n; 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) * n + behind] += prob; } else if (stay > behind) { ans += rank_1 * prob; nxt[stay * n + behind] += prob / 2; nxt[stay * n + behind + 1] += prob / 2; } else { assert(rank_0 == rank_1); ans += rank_0 * prob; // どちらでもよい nxt[(stay + 1) * n + behind] += prob / 2; nxt[stay * n + behind] += prob / 4; nxt[stay * n + behind + 1] += prob / 4; } } dp.swap(nxt); std::cout << ans.val() << " \n"[player + 1 == n]; } return 0; }