結果
| 問題 |
No.3055 Simple Chicken Game
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2025-02-05 21:42:18 |
| 言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 8 ms / 2,000 ms |
| コード長 | 4,784 bytes |
| コンパイル時間 | 15,666 ms |
| コンパイル使用メモリ | 377,156 KB |
| 実行使用メモリ | 5,248 KB |
| 最終ジャッジ日時 | 2025-02-05 21:42:38 |
| 合計ジャッジ時間 | 18,306 ms |
|
ジャッジサーバーID (参考情報) |
judge5 / judge4 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 2 |
| other | AC * 30 |
ソースコード
#include <bits/stdc++.h>
template <typename T, typename S>
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<std::vector<std::pair<mint, mint>>> dp(n + 1, std::vector<std::pair<mint, mint>>(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<std::vector<mint>> prob(n + 1, std::vector<mint>(3, 0));
prob[0][1] = 1;
std::vector<mint> ans(n, 0);
// i - 1 人目までで成功した人数の期待値
// 順位の導出式を眺めると、(投げない - 失敗) の値に関わらず cum[i] + (i - cum[i]) / 2 が順位に加算されるのでそのまま使える
std::vector<mint> 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();
}