P9308 题解 / 有趣莫反题
提供一个
先把
接着我们考虑消去
枚举
枚举
枚举
我们把内层求和设为
可以发现我们要求的就是:
下面我们看下如何快速求
利用莫反处理
我们再次将内层求和设为
则有:
下面考虑
先剔除
接着枚举
酱紫就推完啦!
至于
可以线性筛。
我们总结一下步骤:
-
线性递推求逆元;
-
线性筛出
\tau(1),\tau(2),\cdots,\tau(n) 的值; -
求出
h(1),h(2),\cdots,h(n) 的值。 -
对
h(n)/n^2 做 Dirichlet 差分,得到g(1)/1^2,\cdots,g(n)/n^2 ; -
对
g(n)/n 做 Dirichlet 前缀和,即得f(1)/1,\cdots,f(n)/n 。
总时间复杂度是
代码如下:
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int Mx = 1e6 + 100, Mod = 998244353;
bool Vis[Mx];
int Tau[Mx], Prime[Mx], tot;
int Inv[Mx];
void Sieve(){
Tau[1] = 1;
for(int i = 2; i < Mx; ++i){
if(!Vis[i]) Prime[++tot] = i, Tau[i] = 2 * i - 1;
for(int j = 1; j <= tot && Prime[j] * i < Mx; ++j){
Vis[i * Prime[j]] = true;
if(i % Prime[j] == 0){
Tau[i * Prime[j]] = Prime[j] * (2 * Tau[i] - Prime[j] * Tau[i / Prime[j]]);
break;
}
Tau[i * Prime[j]] = Tau[i] * Tau[Prime[j]];
}
}
}
void GetInv(){
Inv[1] = 1;
for(int i = 2; i < Mx; i++) Inv[i] = -1ll * Inv[Mod % i] * (Mod / i) % Mod;
}
int H[Mx];
int main(){
Sieve(), GetInv();
int n; cin >> n;
int H0 = 0, H1 = 0;
for(int i = 1; i <= n; ++i){
H0 = (H0 + Tau[i]) % Mod;
H1 = (H1 + 1ll * i * Tau[i]) % Mod;
H[i] = (H0 - 1ll * Inv[i] * H1 - (i - 1ll) * (i + 1ll) % Mod * Inv[6]) % Mod * Inv[i] % Mod;
}
for(int i = 1; Prime[i] <= n; ++i){
for(int j = n / Prime[i]; j; --j){
H[j * Prime[i]] = (H[j * Prime[i]] - H[j]) % Mod;
}
}
for(int i = 1; i <= n; ++i) H[i] = 1ll * i * H[i] % Mod;
for(int i = 1; Prime[i] <= n; ++i){
for(int j = 1; j * Prime[i] <= n; ++j){
H[j * Prime[i]] = (H[j * Prime[i]] + H[j]) % Mod;
}
}
for(int i = 1; i <= n; ++i){
int Ans = 1ll * H[i] * i % Mod;
if(Ans < 0) Ans += Mod;
printf("%d ", Ans);
}
return 0;
}