問題の意味は:
まず、\(N-P_1 ^ = {P_2のK_1は}} ^ {K_2 \はcdots P_M K_m} ^ {\)の定義\(F(N)+ = K_1 K_2 + \ cdots K_m + \) 。
今計算
\ [\ sum_ {i = 1 } ^ NF(I!)\%998244353 \]を
アイデア:
最初の通知\(F \)関数このような性質を有する:\(F(AB&)= F(A)+ F(B)\) 。
:だから我々は簡素化が式を持って尋ねる
。(!I)。\ [\ {開始}&\ sum_ = {I} 1 ^ NF&\\ = \ sum_ 1} ^ {N-I = \ sum_ {J. 1 =整列します} ^ IF(J)\\ = &\ sum_ {i = 1} ^ n(nは-I + 1)F(I)= \\&(N + 1)\ sum_ {i = 1} ^ NF(I ) - \ sum_ {i = 1 } ^ N IF(I)\\ \端{整列} \]
ことに注意してください\(F \)は、上記の特性によれば、製品の機能ではなく、我々が発見\(\ sum_ {i = 1 } ^ NF(I)\)が実際に必要とされる\(1,2、\ cdots、nは\) 、インデックスと素因数の数のそれぞれ。上と上の\(N \!)質の分解を行い、我々はそれを変換することができ、ターン素数の各の寄与を考慮するだけです:\ sum_ {i = 1} ^ N- [I \((+ N-1) \ P IN] \ sum_ {K
= 1} ^ {34} \ lfloor \ FRAC {n}は{I ^ K} \ rfloor \) とそれの後半?
あるいは、上記のように、順番に考慮される各素数。素数のために仮定する(P \)\、それは、すべての寄与である\(CDOT P \ P、2、\ cdots、\ lfloor \ {N-FRAC {P}} \ rfloor \ CDOT P \) 、各\ (F \)寄与\(1 \)は、答えは\((1 + 2 + \ cdots + \ lfloor \ FRAC {N-} {P} \ rfloor)P \) ;のための\(P ^ 2 \)それぞれのための\(F \)への貢献\(2 \)が、前に\(Pの\)時間は、それが寄与ので、一度カウントした(1 \)を\、結果は上記と同様です。
要約すると、式の最終的なプッシュは、次のように
\ [(N + 1)\ sum_ {i = 1} ^ N [iがPで\] \ sum_ {k = 1} ^ {34} \ lfloor \ FRAC {n}は{I ^ K} \ rfloor- \ sum_ {i = 1} ^ N [iがPで\] \ sum_ {k = 1} ^ {34} \ FRAC {\ lfloorの\のFRAC {n}は{I ^ K} N \ rfloor(\ lfloor \ FRAC {} { I ^ K} \ rfloor + 1)} {2} ^ iは、k個の\]
ときことを見出した(K> 1 \)を\の行に書かれた上記の式によれば、ライン上で直接暴力考え、うまく処理します。
場合\(K = 1 \)各素数のために必要とされるように、それは直接ことができる\(min25 \)ふるいは、彼の方法を実施します。
う何も詳細を感じ、それを詳細にコードを参照(min25の\を)\行に。(ただし、私は朝の調整、リニアふるいが間違って見つけることができませんでした...)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5, MOD = 998244353, inv = 499122177;
ll n, z;
bool chk[N];
int prime[N], tot;
ll p[N];
void pre() {
for(int i = 2; i <= z; i++) {
if(!chk[i]) {
prime[++tot] = i;
p[tot] = (p[tot - 1] + i) % MOD;
}
for(int j = 1; j <= tot && 1ll * i * prime[j] <= z; j++) {
chk[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
}
ll w[N], g1[N], g2[N];
int ind[N], ind2[N];
int cnt;
void calc_g() {
for(ll i = 1, j; i <= n; i = j + 1) {
j = n / (n / i);
w[++cnt] = n / i;
if(w[cnt] <= z) ind[w[cnt]] = cnt;
else ind2[n / w[cnt]] = cnt;
g1[cnt] = (w[cnt] - 1) % MOD;
g2[cnt] = w[cnt] % MOD * ((w[cnt] + 1) % MOD) % MOD * inv % MOD - 1;
}
for(int i = 1; i <= tot; i++) {
for(int j = 1; j <= cnt && 1ll * prime[i] * prime[i] <= w[j]; j++) {
ll tmp = w[j] / prime[i], k;
if(tmp <= z) k = ind[tmp]; else k = ind2[n / tmp];
g1[j] -= (g1[k] - i + 1) % MOD;
g2[j] -= 1ll * (p[i] - p[i - 1]) * (g2[k] - p[i - 1]) % MOD;
g1[j] %= MOD; g2[j] %= MOD;
if(g1[j] < 0) g1[j] += MOD;
if(g2[j] < 0) g2[j] += MOD;
}
}
}
ll work() {
ll ans = 0;
for(ll i = 1, j; i <= n; i = j + 1) {
j = n / (n / i);
ll l = ((i - 1 <= z) ? ind[i - 1] : ind2[(n / (i - 1))]);
ll r = ((j <= z) ? ind[j] : ind2[n / j]);
ans += (n / i) % MOD * ((n + 1) % MOD) % MOD * (g1[r] - g1[l]) % MOD;
ans -= (n / i) % MOD * ((n / i + 1) % MOD) % MOD * inv % MOD * (g2[r] - g2[l]) % MOD;
ans = (ans % MOD + MOD) % MOD;
}
for(int i = 1; i <= tot; i++) {
ll prim = prime[i];
for(; prim * prime[i] <= n;) {
prim *= prime[i];
ans += (n + 1) % MOD * ((n / prim) % MOD) % MOD;
ans %= MOD;
ans -= (n / prim) % MOD * (n / prim + 1) % MOD * inv % MOD * prim % MOD;
ans %= MOD;
}
}
if(ans < 0) ans += MOD;
return ans;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n; z = sqrt(n) + 1;
pre();
calc_g();
cout << work();
return 0;
}