题目描述
给定数论函数 f 和正整数 n,求 ∑ni=1fi 的值。
解决方法
设
S(n)=n∑i=1fi
找一个合适的数论函数 g ,则
n∑i=1(f∗g)(i)=n∑i=1g(i)⌊ni⌋∑j=1f(j)=n∑i=1g(i)S(⌊ni⌋)
有
g(1)S(n)=n∑i=1(f∗g)(i)−n∑i=2g(i)S(⌊ni⌋)
若能找到一个数论函数 g 满足单点计算复杂度为 O(1) 且卷积计算 f∗g 复杂度也是 O(1) ,那么可以用杜教筛快速求出 S(n) 的值。
预处理后总时间复杂度为 O(n23) 。
例题
计算 ∑ni=1μ(i) 和 ∑ni=1φ(i)的值。
由于 μ∗I=ε ,设 g=I,f∗g=ε 。
得到
S(n)=1−n∑i=2S(⌊ni⌋)
由于 φ∗I=Id ,设 g=I,f∗g=Id 。
得到
S(n)=n∗(n+1)2−n∑i=2S(⌊ni⌋)
实现
以 μ 和 φ 的前缀和为例。
加了预处理和记忆化保证时间复杂度。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10, maxn = 1e6;
LL phi[N], mu[N];
int p[N], tot;
bool vis[N];
unordered_map<int, LL> Sphi, Smu;
void init() {
mu[1] = phi[1] = 1;
for (int i = 2; i <= maxn; i ++ ) {
if (!vis[i]) {
p[++ tot] = i;
phi[i] = i - 1, mu[i] = -1;
}
for (int j = 1; p[j] * i <= maxn; j ++ ) {
vis[p[j] * i] = true;
if (i % p[j] == 0) {
phi[p[j] * i] = phi[i] * p[j];
break;
}
phi[p[j] * i] = phi[i] * (p[j] - 1);
mu[p[j] * i] = -mu[i];
}
}
for (int i = 1; i <= maxn; i ++ ) mu[i] += mu[i - 1], phi[i] += phi[i - 1];
}
LL Get_Sphi(LL n) {
if (n <= maxn) return phi[n];
if (Sphi[n]) return Sphi[n];
LL res = n * (n + 1) / 2;
for (LL l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
res -= (r - l + 1) * Get_Sphi(n / l);
}
return Sphi[n] = res;
}
LL Get_Smu(LL n) {
if (n <= maxn) return mu[n];
if (Smu[n]) return Smu[n];
LL res = 1;
for (LL l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
res -= (r - l + 1) * Get_Smu(n / l);
}
return Smu[n] = res;
}
int main() {
init();
int T;
scanf("%d", &T);
while (T -- ) {
LL n;
scanf("%lld", &n);
printf("%lld %lld\n", Get_Sphi(n), Get_Smu(n));
}
return 0;
}
orz
捕捉野生大佬