結果
| 問題 |
No.3364 Push_back Operation
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2025-10-27 15:49:19 |
| 言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
TLE
|
| 実行時間 | - |
| コード長 | 5,464 bytes |
| コンパイル時間 | 1,043 ms |
| コンパイル使用メモリ | 117,444 KB |
| 実行使用メモリ | 15,944 KB |
| 最終ジャッジ日時 | 2025-11-17 20:33:40 |
| 合計ジャッジ時間 | 4,443 ms |
|
ジャッジサーバーID (参考情報) |
judge2 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | -- * 2 |
| other | AC * 1 TLE * 1 -- * 51 |
ソースコード
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
// 剰余
const int MOD = 998244353;
using ll = long long;
// べき乗計算
ll power(ll base, ll exp) {
ll res = 1;
base %= MOD;
while (exp > 0) {
if (exp & 1) res = (res * base) % MOD;
base = (base * base) % MOD;
exp >>= 1;
}
return res;
}
// 逆元計算 (フェルマーの小定理)
ll mod_inv(ll n) {
return power(n, MOD - 2);
}
// --------------------------------------------------
// 前計算: メビウス関数 \mu(d)
// --------------------------------------------------
ll MAX_MU_LIMIT;
vector<int> mu;
void sieve_mu(ll limit) {
limit = min(limit, (ll)320000);
MAX_MU_LIMIT = limit;
mu.resize(limit + 1);
vector<int> min_prime(limit + 1, 0);
vector<int> primes;
mu[1] = 1;
for (int i = 2; i <= limit; ++i) {
if (min_prime[i] == 0) {
min_prime[i] = i;
primes.push_back(i);
mu[i] = -1;
}
for (int p : primes) {
if (p > min_prime[i] || (ll)i * p > limit) break;
min_prime[i * p] = p;
if (p == min_prime[i]) {
mu[i * p] = 0;
} else {
mu[i * p] = -mu[i];
}
}
}
}
// --------------------------------------------------
// T の約数 L のベキ乗和の総和を O(\sqrt{T_{max}} \log N) で計算する関数
// \sum_{T=1}^{T_{max}} \sum_{L \mid T} Y^L
// --------------------------------------------------
ll sum_L_powers_total(ll T_max, ll Y) {
ll total_sum = 0;
if (T_max == 0 || Y == 0) return 0;
// Lに関する総和を \lfloor T_{max}/L \rfloor が一定になる区間に分割 (O(\sqrt{T_{max}}))
// このとき、\lfloor T_{max}/L \rfloor は、T=L*kのkの最大値に相当する
for (ll L_start = 1; L_start <= T_max; ) {
ll count = T_max / L_start; // T=L*k の k の最大値
ll L_end = T_max / count;
// 区間 [L_start, L_end] での Y^L の総和を等比数列の和で計算 (O(\log N))
ll sum_L = 0;
if (Y == 1) {
// Y=1 の場合: L_end - L_start + 1
sum_L = (L_end - L_start + 1) % MOD;
} else {
// Y > 1 の場合: 等比数列の和
ll start_term = power(Y, L_start);
ll num_terms = L_end - L_start + 1;
ll ratio_power = power(Y, num_terms);
ll numerator = (ratio_power - 1 + MOD) % MOD;
ll denominator_inv = mod_inv(Y - 1);
ll geometric_sum = (start_term * numerator) % MOD;
geometric_sum = (geometric_sum * denominator_inv) % MOD;
sum_L = geometric_sum;
}
// 寄与: count * \sum_{L=L_{start}}^{L_{end}} Y^L
// count は T が L の倍数となる個数
ll term = (count % MOD) * sum_L % MOD;
total_sum = (total_sum + term) % MOD;
L_start = L_end + 1;
}
return total_sum;
}
// --------------------------------------------------
// f(T) = \sum_{d=1}^{\lfloor N/T \rfloor} \mu(d) \left( \sum_{L \mid T} \left( \lfloor \frac{\lfloor N/T \rfloor}{d} \rfloor \right)^L \right) の累積和を計算
// G(T_max) = \sum_{T=1}^{T_{max}} f(T)
// --------------------------------------------------
ll G(ll N, ll T_max) {
if (T_max == 0) return 0;
ll total_sum = 0;
// Tに関する総和を \lfloor N/T \rfloor = X が一定な区間に分割 (O(\sqrt{N}))
// この区間 T \in [T_start, T_end] の総和は S_X
for (ll T_start = 1; T_start <= T_max; ) {
ll X = N / T_start;
ll T_end = min(T_max, N / X); // 区間の終点は T_max で打ち切り
// dに関する総和: \sum_{d=1}^{X} \mu(d) \cdot S_{T}(d)
ll current_block_sum = 0;
// d の総和の上限は X だが、\mu(d) の前計算は SQRT_N まで
ll d_limit = min(X, MAX_MU_LIMIT);
for (ll d = 1; d <= d_limit; ++d) {
if (mu[d] == 0) continue;
ll Y = X / d; // \lfloor X/d \rfloor
// T_startからT_endまでの区間での \sum_{T} \sum_{L \mid T} Y^L の計算
// sum_L_powers_total(T_max, Y) = \sum_{T=1}^{T_{max}} \sum_{L \mid T} Y^L
// T_end の累積和から T_start-1 の累積和を引く (差分計算)
// \sum_{T=T_{start}}^{T_{end}} \sum_{L \mid T} Y^L
ll sum_T_L_end = sum_L_powers_total(T_end, Y);
ll sum_T_L_start_minus_1 = sum_L_powers_total(T_start - 1, Y);
ll sum_T_L = (sum_T_L_end - sum_T_L_start_minus_1 + MOD) % MOD;
ll term = (mu[d] == 1) ? sum_T_L : (MOD - sum_T_L) % MOD;
current_block_sum = (current_block_sum + term) % MOD;
}
total_sum = (total_sum + current_block_sum) % MOD;
T_start = T_end + 1;
}
return total_sum;
}
// --------------------------------------------------
// メイン関数
// --------------------------------------------------
void solve() {
ll N;
if (!(cin >> N)) return;
ll SQRT_N = sqrt(N);
sieve_mu(SQRT_N);
// 求める総数は G(N) = \sum_{T=1}^{N} f(T)
ll result = G(N, N);
cout << result << endl;
}
int main() {
// 高速化のために入出力を解除
ios_base::sync_with_stdio(false);
cin.tie(NULL);
solve();
return 0;
}