#include #include #include #include #include // https://yukicoder.me/submissions/1039329 から以下を改善した // - /2 と /4 を前計算した int main() { using mint = atcoder::modint; 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); const mint inv2 = mint::raw(2).inv(), inv4 = inv2 * inv2; // dp_hoge[i][j][k] は以下を前提とする // - i: まだ操作していないプレイヤーの人数 // - j: これまで操作しなかったプレイヤーの人数 // - これまでに操作した裏が出たプレイヤーの人数が j - 1 + k 人 // dp_noop := 操作しなかったプレイヤーのうち、 // 現時点で最下位のプレイヤーにおける最終的な順位の期待値 std::vector>> dp_noop(n + 1); for (int yet = 0; yet <= n; ++yet) { dp_noop[yet] = std::vector((n - yet + 1) / 2 + 1, std::vector(3, mint())); for (int noop = 0; noop <= (n - yet + 1) / 2; ++noop) { for (int k = std::max(-noop, -1); k <= 1; ++k) { const int flip = noop + k; if (noop + yet + flip > n) continue; if (yet == 0) { dp_noop[yet][noop][k + 1] = noop; continue; } // 次のプレイヤーの選択を考える if (noop < flip) { // 操作しない dp_noop[yet][noop][k + 1] = dp_noop[yet - 1][noop + 1][k] - 1; } else if (noop > flip) { // 操作する // 表 dp_noop[yet][noop][k + 1] += (dp_noop[yet - 1][noop][k + 1] + 1) * inv2; // 裏 dp_noop[yet][noop][k + 1] += dp_noop[yet - 1][noop][k + 2] * inv2; } else { // どちらでもよい dp_noop[yet][noop][k + 1] += (dp_noop[yet - 1][noop + 1][k] - 1) * inv2; dp_noop[yet][noop][k + 1] += (dp_noop[yet - 1][noop][k + 1] + 1) * inv4; dp_noop[yet][noop][k + 1] += dp_noop[yet - 1][noop][k + 2] * inv4; } } } } // dp_flip := 操作して裏が出たプレイヤーのうち、 // 現時点で最下位のプレイヤーにおける最終的な順位の期待値 std::vector>> dp_flip(n + 1); for (int yet = 0; yet <= n; ++yet) { dp_flip[yet] = std::vector((n - yet + 1) / 2 + 1, std::vector(3, mint())); for (int noop = 0; noop <= (n - yet + 1) / 2; ++noop) { for (int k = std::max(-noop, -1); k <= 1; ++k) { const int flip = noop + k; if (noop + yet + flip > n) continue; if (yet == 0) { dp_flip[yet][noop][k + 1] = noop + flip; continue; } // 次のプレイヤーの選択を考える if (noop < flip) { // 操作しない dp_flip[yet][noop][k + 1] = dp_flip[yet - 1][noop + 1][k]; } else if (noop > flip) { // 操作する // 表 dp_flip[yet][noop][k + 1] += (dp_flip[yet - 1][noop][k + 1] + 1) * inv2; // 裏 dp_flip[yet][noop][k + 1] += (dp_flip[yet - 1][noop][k + 2] - 1) * inv2; } else { // どちらでもよい dp_flip[yet][noop][k + 1] += dp_flip[yet - 1][noop + 1][k] * inv2; dp_flip[yet][noop][k + 1] += (dp_flip[yet - 1][noop][k + 1] + 1) * inv4; dp_flip[yet][noop][k + 1] += (dp_flip[yet - 1][noop][k + 2] - 1) * inv4; } } } } // dp[i][k] := これまで最適な操作がなされてきたときに以下を満たす確率 // - 操作しなかったプレイヤーが i 人 // - 操作して裏が出たプレイヤーの人数が i - 1 + k 人 std::vector dp(1, std::vector(3, mint())); dp[0][0 + 1] = 1; for (int player = 0; player < n; ++player) { std::vector nxt(player + 2, std::vector(3, mint())); mint ans = 0; const int yet = n - 1 - player; for (int noop = 0; noop <= (player + 1) / 2; ++noop) { for (int k = std::max(-noop, -1); k <= 1; ++k) { const int flip = noop + k; if (noop + flip > player) break; const int tip = player - (noop + flip); // 表を出したプレイヤーの人数 const mint prob = dp[noop][k + 1]; // 現在のプレイヤーの選択を考える if (noop < flip) { // 操作しない ans += (tip + dp_noop[yet][noop + 1][k]) * prob; nxt[noop + 1][k] += prob; } else if (noop > flip) { // 操作する // 表 ans += (tip + 1) * prob * inv2; nxt[noop][k + 1] += prob * inv2; // 裏 ans += (tip + dp_flip[yet][noop][k + 2]) * prob * inv2; nxt[noop][k + 2] += prob * inv2; } else { // どちらでもよい assert(dp_noop[yet][noop + 1][k] == (1 + dp_flip[yet][noop][k + 2]) * inv2); ans += (tip + dp_noop[yet][noop + 1][k]) * prob; nxt[noop + 1][k] += prob * inv2; nxt[noop][k + 1] += prob * inv4; nxt[noop][k + 2] += prob * inv4; } } } dp.swap(nxt); std::cout << ans.val() << " \n"[player + 1 == n]; } return 0; }