結果
問題 | No.3055 Simple Chicken Game |
ユーザー |
![]() |
提出日時 | 2025-01-26 19:11:32 |
言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 1,516 ms / 2,000 ms |
コード長 | 5,215 bytes |
コンパイル時間 | 2,759 ms |
コンパイル使用メモリ | 114,028 KB |
実行使用メモリ | 250,368 KB |
最終ジャッジ日時 | 2025-02-05 00:30:00 |
合計ジャッジ時間 | 11,505 ms |
ジャッジサーバーID (参考情報) |
judge1 / judge4 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 2 |
other | AC * 30 |
ソースコード
#include <algorithm> #include <cassert> #include <iostream> #include <vector> #include <atcoder/modint> // 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<std::vector<std::vector<mint>>> 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<std::vector<std::vector<mint>>> 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; }