积性函数
如果一个数论函数(f(n)),满足(n,m)互质,那么有(f(n*m) = f(n)*f(m)),那么(f(n))为积性函数
特别地,如果对于任意(n,m)都满足(f(n*m) = f(n)*f(m)),那么(f(n))为完全积性函数
常见的积性函数有

狄利克雷卷积
对于两个积性函数(f(n),g(n)),定义它们的狄利克雷卷积为:((f*g)(n) = sum_{d|n} f(d) * g(frac{n}{d}))
常用的几种卷积关系:
$mu * 1 = epsilon $
(phi * 1=Idquad quadvarphi = Id * mu)
(d = 1 * 1quad quad 1 = mu*d)
杜教筛
以低于线性的时间复杂度计算积性函数前缀和
假设要求的积性函数(f)的前缀和为(S)
构造两个辅助函数:积性函数(g),狄利克雷卷积(f*g)
有结论(sum_{i = 1}^n(f*g)(i) = sum_{i = 1}^ng(i)S(frac{n}{i}))
变式:
只需要找到一个(g(n))使得(sum_{i = 1}^n(f*g)(i))可以被快速计算,且(g(n))的前缀和容易求
那么可以分块+递归求出(S(n))
莫比乌斯函数前缀和
(S(n) = sum_{i= 1}^nmu(i))
(g(1)S(n) = sum_{i = 1}^n(f*g)(i) - sum_{i = 2}^ng(i)S(frac{n}{i}))
选择(g = 1(i)),那么对于((f*g)(i) = sum_{d|i}1*mu(frac{i}{d}) = epsilon)
又(sum_{j|i}mu(j)= left{egin{array}{ll} 0 & i geq 2 \ 1 & i=1 end{array} ight.)
(S(n) = 1 - sum_{i = 2}^nS(lfloorfrac{n}{i} floor))
欧拉函数前缀和
(S(n) = sum_{i = 1}^nvarphi(i))
(g(1)S(n) = sum_{i = 1}^n(f*g)(i) - sum_{i = 2}^ng(i)S(frac{n}{i}))
选择(g = 1(i)),那么对于((f*g)(i) = sum_{d|i}1 * varphi(frac{i}{d}) = Id)
又(sum_{i = 1}^nId = frac{n(n+1)}{2})
(S(n) = frac{n(n+1)}{2} - sum_{i=2}^nS(lfloorfrac{n}{i} floor))
模板
#include <iostream>
#include <cstdio>
#include <unordered_map>
#define ll long long
using namespace std;
const int N = 3e6+5;
bool vis[N];
ll phi[N + 5];
ll mu[N + 5], pri[N];
int tot = 0;
unordered_map<int,ll>ans_p,ans_m;
void table(int N){
mu[1] = 1,phi[1] = 1;vis[1] = 1;
for(int i = 2; i <= N; i++){
if(!vis[i]){
pri[++tot] = i;
mu[i] = -1;phi[i] = i - 1;
}
for(int j = 1; j <= tot; j++){
if(i * pri[j] > N)break;
vis[i * pri[j]] = 1;
if(i % pri[j] == 0){
mu[i * pri[j]] = 0;
phi[i * pri[j]] = phi[i] * pri[j];
break;
}else{
mu[i * pri[j]] = -mu[i];
phi[i * pri[j]] = phi[i] * phi[pri[j]];
}
}
}
for(int i = 1; i <= N; i++){
phi[i] += phi[i - 1];
mu[i] += mu[i - 1];
}
}
ll sum_p(int n){
if(n <= N)return phi[n];
if(ans_p[n])return ans_p[n];
ll ans = (ll)n * (n + 1)/2;
for(int l = 2,r; l <= n; l = r + 1){
r = n/(n/l);
ans -= sum_p(n/l) * (r - l + 1);
}
return ans_p[n] = ans;
}
ll sum_m(int n){
if(n <= N)return mu[n];
if(ans_m[n])return ans_m[n];
ll ans = 1;
for(int l = 2,r; l <= n; l = r + 1){
r = n/(n/l);
ans -= sum_m(n/l) * (r - l + 1);
}
return ans_m[n] = ans;
}
int main(){
table(N);
int n;
scanf("%d",&n);
printf("%lld %lld
", sum_m(n),sum_p(n));
return 0;
}