#include template T modpow(T a, S b, T MOD) { T ret = 1; while (b > 0) { if (b & 1) { ret *= a; ret %= MOD; } a *= a; a %= MOD; b >>= 1; } return ret; } bool isPrime(long long n) { if (n <= 1) return false; else if (n == 2) return true; else if (n % 2 == 0) return false; long long A[7] = {2, 325, 9375, 28178, 450775, 9780504, 1795265022}; long long s = 0; long long d = n - 1; while (d % 2 == 0) { d /= 2; s++; } for (auto a : A) { if (a % n == 0) return true; long long x = modpow<__int128_t>(a, d, n); if (x != 1) { bool ng = true; for (int i = 0; i < s; i++) { if (x == n - 1) { ng = false; break; }; x = __int128_t(x) * x % n; } if (ng) return false; } } return true; } #include "atcoder/modint.hpp" using mint = atcoder::modint; #include "testlib.h" void solve() { int n = inf.readInt(1, 3000); inf.readSpace(); int P = inf.readInt(100'000'000, 1'000'000'000); inf.readEoln(); assert(isPrime(P)); mint::set_mod(P); // dp[i] := i 番目のプレイヤーの手番、(投げない - 失敗 + 1) = j の時の i 人目以降の(成功、投げない) の回数の期待値 std::vector>> dp(n + 1, std::vector>(3, {0, 0})); for (int i = n - 1; i >= 0; i--) { // j = 0 -> 投げない dp[i][0].first = dp[i + 1][1].first; dp[i][0].second = dp[i + 1][1].second + 1; // j = 1 -> どちらでも dp[i][1].first = dp[i + 1][2].first / 2 + (dp[i + 1][0].first + 1 + dp[i + 1][1].first) / 4; dp[i][1].second = (dp[i + 1][2].second + 1) / 2 + (dp[i + 1][0].second + dp[i + 1][1].second) / 4; // j = 2 -> 投げる dp[i][2].first = (dp[i + 1][1].first + 1 + dp[i + 1][2].first) / 2; dp[i][2].second = (dp[i + 1][1].second + dp[i + 1][2].second) / 2; } std::vector> prob(n + 1, std::vector(3, 0)); prob[0][1] = 1; std::vector ans(n, 0); // i - 1 人目までで成功した人数の期待値 // 順位の導出式を眺めると、(投げない - 失敗) の値に関わらず cum[i] + (i - cum[i]) / 2 が順位に加算されるのでそのまま使える std::vector cum(n + 1, 0); for (int i = 0; i < n; i++) { if (i != 0) { cum[i] += cum[i - 1]; } // j = 0 -> 投げない { prob[i + 1][1] += prob[i][0]; mint c = 1; c += cum[i]; // 直前までで成功 c += mint(i - cum[i] - 1) / 2; // 直前までで投げない c += dp[i + 1][1].first; // 以降で成功 ans[i] += prob[i][0] * c; } // j = 1 -> どちらでも { mint c = 1; // 投げる prob[i + 1][0] += prob[i][1] / 4; prob[i + 1][1] += prob[i][1] / 4; c += cum[i] / 2; // 直前までで成功 c += mint(i - cum[i]) / 4; // 自身が失敗・直前までで投げない c += (dp[i + 1][0].first + dp[i + 1][0].second) / 4; // 自身が失敗・以降で成功 or 投げない cum[i + 1] += prob[i][1] / 4; // 投げない prob[i + 1][2] += prob[i][1] / 2; c += cum[i] / 2; // 直前までで成功 c += mint(i - cum[i]) / 4; // 直前までで投げない c += dp[i + 1][2].first / 2; // 以降で成功 ans[i] += prob[i][1] * c; } // j = 2 -> 投げる { mint c = 1; prob[i + 1][1] += prob[i][2] / 2; prob[i + 1][2] += prob[i][2] / 2; c += cum[i]; // 直前までで成功 c += mint(i - cum[i]) / 2; // 自身が失敗・直前までで投げない c += (dp[i + 1][1].first + dp[i + 1][1].second) / 2; // 自身が失敗・以降で成功 or 投げない cum[i + 1] += prob[i][2] / 2; ans[i] += prob[i][2] * c; } } for (size_t i = 0; i < ans.size(); i++) { std::cout << ans[i].val() << (i + 1 == ans.size() ? '\n' : ' '); } } int main(int argc, char *argv[]) { std::cin.tie(0)->sync_with_stdio(0); registerValidation(argc, argv); solve(); inf.readEof(); }