結果
| 問題 |
No.3055 Simple Chicken Game
|
| コンテスト | |
| ユーザー |
emthrm
|
| 提出日時 | 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;
}
emthrm