下面的所有知识均属数论范畴。
欧拉筛
可以做到 O ( n ) O(n) O(n) 的线性复杂度!
可以筛所有的积性函数(即对于所有的 a ⊥ b a \perp b a⊥b 都满足 f ( a b ) = f ( a ) ∗ f ( b ) f(ab) = f(a)*f(b) f(ab)=f(a)∗f(b) 的函数)。
举两个例子:
线性筛欧拉函数:
inline void Sieve()
{
phi[1] = 1;
for(register int i = 2; i <= MAXN; i++) //MAXN表示值域
{
if(!NotPrime[i]) //是质数
{
phi[i] = i - 1;
Prime[++len] = i;
}
for(register int j = 1; j <= len && i * Prime[j] <= MAXN; j++)
{
NotPrime[i * Prime[j]] = true;
if(i % Prime[j] == 0) //此时 Prime[j] 为 i 的最小质因子
{
phi[i * Prime[j]] = phi[i] * prime[j];
break;
}
phi[i * Prime[j]] = phi[i] * (Prime[j] - 1);
}
}
return;
}
线性筛莫比乌斯函数:
inline void Sieve()
{
miu[1] = 1;
for(register int i = 2; i <= MAXN; i++)
{
if(!NotPrime[i])
{
miu[i] = -1;
Prime[++len] = i;
}
for(register int j = 1; j <= len && i * Prime[j] <= MAXN; j++)
{
NotPrime[i * Prime[j]] = true;
if(i % Prime[j] == 0) break; //此时 miu 函数值为 0,可以直接跳过
miu[i * Prime[j]] = -miu[i];
}
}
return;
}
可以发现,每个数最多只会被自己的最小质因子筛到一次,所以时间复杂度是线性的 O ( n ) O(n) O(n)。
不过要注意,每个数被自己的最小质因子筛到时,对应的 i * Prime[j]仍然是需要计算函数值的!(当然
μ
\mu
μ 函数此时就等于
0
0
0,可以直接跳过)
杜教筛
在非线性时间内求解积性函数的前缀和。
设现在我们要求的是 f f f 的前缀和,即 S ( n ) = ∑ i = 1 n f ( i ) S(n) = \sum_{i = 1} ^n f(i) S(n)=∑i=1nf(i)。
我们考虑把
f
f
f 卷上一个
g
g
g 的前缀和,即:
S
′
(
n
)
=
∑
i
=
1
n
(
f
∗
g
)
(
i
)
=
∑
i
=
1
n
∑
d
∣
i
f
(
d
)
g
(
i
d
)
=
∑
d
=
1
n
g
(
d
)
∑
i
=
1
⌊
n
d
⌋
f
(
i
)
=
∑
d
=
1
n
g
(
d
)
S
(
⌊
n
d
⌋
)
\begin{aligned} S'(n) &= \sum_{i = 1}^n(f * g)(i) \\ &= \sum_{i = 1} ^n \sum_{d | i} f(d)g(\frac{i}{d}) \\ &=\sum_{d = 1}^n g(d) \sum_{i = 1}^{\lfloor\frac{n}{d}\rfloor} f(i) \\ &=\sum_{d = 1}^ng(d)S(\lfloor\frac{n}{d}\rfloor) \end{aligned}
S′(n)=i=1∑n(f∗g)(i)=i=1∑nd∣i∑f(d)g(di)=d=1∑ng(d)i=1∑⌊dn⌋f(i)=d=1∑ng(d)S(⌊dn⌋)
然后再推导一下,发现:
g
(
1
)
S
(
n
)
=
∑
d
=
1
n
g
(
d
)
S
(
⌊
n
d
⌋
)
−
∑
d
=
2
n
g
(
d
)
S
(
⌊
n
d
⌋
)
g(1)S(n) = \sum_{d = 1}^ng(d)S(\lfloor\frac{n}{d}\rfloor) - \sum_{d = 2}^ng(d)S(\lfloor\frac{n}{d}\rfloor)
g(1)S(n)=d=1∑ng(d)S(⌊dn⌋)−d=2∑ng(d)S(⌊dn⌋)
即:
S
(
n
)
=
∑
i
=
1
n
(
f
∗
g
)
(
i
)
−
∑
d
=
2
n
g
(
d
)
S
(
⌊
n
d
⌋
)
g
(
1
)
S(n) = \frac{\sum_{i = 1}^n(f * g)(i) - \sum_{d = 2}^ng(d)S(\lfloor\frac{n}{d}\rfloor)}{g(1)}
S(n)=g(1)∑i=1n(f∗g)(i)−∑d=2ng(d)S(⌊dn⌋)
所以,只要我们找到合适的
g
g
g,就能够快速求出
S
(
n
)
S(n)
S(n) 了。
(这个时候建议看看下文的莫反公式)
对于 μ \mu μ 的前缀和,我们发现有 μ ∗ 1 = ϵ \mu * 1 = \epsilon μ∗1=ϵ,即定义 g = 1 g = 1 g=1,代入公式有 S ( n ) = 1 − ∑ i = 2 n S ( n i ) S(n) = 1 - \sum_{i = 2}^nS(\frac{n}{i}) S(n)=1−∑i=2nS(in)。
对于 φ \varphi φ 的前缀和,我们发现有 φ ∗ 1 = i d \varphi * 1 = id φ∗1=id,即定义 g = 1 g = 1 g=1,代入公式有 S ( n ) = n ( n + 1 ) 2 − ∑ i = 2 n S ( n i ) S(n) = \frac{n(n + 1)}{2} - \sum_{i = 2}^n S(\frac{n}{i}) S(n)=2n(n+1)−∑i=2nS(in)。
这些递推式显然可以整除分块做。
不过为了做到非线性复杂度,我们需要先预处理出 n 2 3 n^{\frac{2}{3}} n32 以内的 S ( n ) S(n) S(n),然后剩下的用记忆化搜索。
这样的复杂度据说可以达到最快的 O ( n 2 3 ) O(n^{\frac{2}{3}}) O(n32)。
贴上洛谷模板题(上面举的两个例子)的代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cassert>
#include<unordered_map>
#define MAXN 5000001
using LL = long long;
using namespace std;
int t, n;
int len, Prime[MAXN];
LL Phi[MAXN], Miu[MAXN];
bool NotPrime[MAXN];
unordered_map<int, LL> AnsPhi, AnsMiu;
inline void Prework()
{
Phi[1] = Miu[1] = 1LL;
for(register int i = 2; i < MAXN; i++)
{
if(!NotPrime[i])
{
Prime[++len] = i;
Phi[i] = i - 1LL;
Miu[i] = -1LL;
}
for(register int j = 1; j <= len && i * Prime[j] < MAXN; j++)
{
NotPrime[i * Prime[j]] = true;
if(i % Prime[j] == 0LL)
{
Phi[i * Prime[j]] = Phi[i] * Prime[j];
break;
}
Phi[i * Prime[j]] = Phi[i] * (Prime[j] - 1LL);
Miu[i * Prime[j]] = -Miu[i];
}
}
for(register int i = 1; i < MAXN; i++) Phi[i] += Phi[i - 1], Miu[i] += Miu[i - 1];
return;
}
inline LL Solve_Phi(int n)
{
if(n < MAXN) return Phi[n];
if(AnsPhi.count(n)) return AnsPhi[n];
LL ans = 1LL * n * (n + 1LL) / 2LL;
for(register LL l = 2LL, r; l <= n; l = r + 1LL)
{
r = n / (n / l);
ans -= 1LL * (r - l + 1LL) * Solve_Phi(n / l);
}
AnsPhi.insert(make_pair(n, ans));
return ans;
}
inline LL Solve_Miu(int n)
{
if(n < MAXN) return Miu[n];
if(AnsMiu.count(n)) return AnsMiu[n];
LL ans = 1LL;
for(register LL l = 2LL, r; l <= n; l = r + 1LL)
{
r = n / (n / l);
ans -= 1LL * (r - l + 1LL) * Solve_Miu(n / l);
}
AnsMiu.insert(make_pair(n, ans));
return ans;
}
int main()
{
#ifdef FILE
freopen("Input.in", "r", stdin);
#endif
Prework();
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
printf("%lld %lld\n", Solve_Phi(n), Solve_Miu(n));
continue;
}
return 0;
}
M i n _ 25 Min\_25 Min_25 筛
一般用于求积性函数 f f f 的前缀和。 f f f 要求当 p p p 为质数时, f ( p ) f(p) f(p) 是一个次数较少的简单多项式,它的每一项都必须是完全积性函数。
复杂度众说纷纭,不过当 n ≤ 1 0 10 n \le 10^{10} n≤1010 时可以轻松跑过。
- S t e p 1 Step~~1 Step 1:
先考虑求质数的前缀和:
∑
i
=
1
,
i
∈
P
r
i
m
e
n
f
(
i
)
\sum_{i = 1,i \in Prime}^n f(i)
i=1,i∈Prime∑nf(i)
我们先来看一个神奇的操作:
考虑当 d d d 为质数时 f ( p ) f(p) f(p) 的运算公式。
然后,我们把所有数都当成质数,套用这个运算公式!
我们设这个新的函数为 h h h。
前面提到当 p p p 为质数时 f ( p ) f(p) f(p) 是一个次数较低的多项式,所以,我们可以把 h h h 拆成几项,分开来算,最后加在一起即可。
我们设 g ( n , j ) g(n, j) g(n,j) 为 1 ∼ n 1\sim n 1∼n 中,所有质数或者最小质因子大于等于第 j j j 个质数的合数的 f f f 值的和,设 p j p_j pj 为第 j j j 个质数。
考虑分讨:
- 如果 p j > n p_j > n pj>n,此时不会再有更多的合数造成负贡献,因此有 g ( n , j ) = g ( n , j − 1 ) g(n, j) = g(n, j - 1) g(n,j)=g(n,j−1);
- 如果 p j ≤ n p_j \le n pj≤n,我们发现包含负贡献的数中一定含有质因子 p j p_j pj,且除掉 p j p_j pj 后最小的质因子会大于等于 p j p_j pj。所以我们考虑减去 h ( p j ) g ( n p j , j − 1 ) h(p_j)g(\frac{n}{p_j},j−1) h(pj)g(pjn,j−1),但 g ( n p j , j − 1 ) g(\frac{n}{p_j},j−1) g(pjn,j−1) 中重复减去了 ∑ i = 1 j − 1 h ( p i ) \sum_{i=1}^{j - 1}h(p_i) ∑i=1j−1h(pi) 这些最小质因子小于 p j p_j pj 的函数值,所以再把它们加上就好了。
此时有递推式:
g
(
n
,
j
)
=
g
(
n
,
j
−
1
)
−
h
(
p
j
)
(
g
(
⌊
n
p
j
⌋
,
j
−
1
)
−
∑
i
=
1
j
−
1
h
(
p
i
)
)
g(n, j) = g(n, j - 1) - h(p_j)(g(\lfloor\frac{n}{p_j}\rfloor,j - 1) - \sum_{i = 1}^{j - 1}h(p_i))
g(n,j)=g(n,j−1)−h(pj)(g(⌊pjn⌋,j−1)−i=1∑j−1h(pi))
最后我们可以求出
g
(
n
,
∣
P
r
i
m
e
∣
)
g(n, |Prime|)
g(n,∣Prime∣),这就是前缀和中所有质数的贡献。
- S t e p 2 Step~2 Step 2:
类⽐ g g g,设 S ( n , j ) S(n, j) S(n,j) 表⽰最⼩质因⼦不⼩于第 j j j 个素数的所有 f f f 的函数值和。
初始时有 S ( n , ∣ P r i m e ∣ ) = g ( n , ∣ P r i m e ∣ ) S(n, |Prime|) = g(n, |Prime|) S(n,∣Prime∣)=g(n,∣Prime∣)。
对于质数,我们已经算出答案;对于合数,我们考虑枚举最小质因子及其指数即可递推:
(注意当
f
(
p
k
t
+
1
)
f(p_k^{t + 1})
f(pkt+1) 超出范围时,我们定义其为
0
0
0。)
S
(
n
,
j
)
=
g
(
n
,
∣
P
r
i
m
e
∣
)
−
∑
i
=
1
j
−
1
f
(
p
i
)
+
∑
k
=
j
p
k
2
≤
n
∑
t
=
1
p
k
t
≤
n
(
f
(
p
k
t
+
1
)
+
f
(
p
k
t
)
S
(
⌊
n
p
k
t
⌋
,
k
+
1
)
)
)
S(n, j) = g(n, |Prime|) - \sum_{i = 1}^{j - 1} f(p_i) + \sum_{k = j}^{p_k^2 \le n}\sum_{t = 1}^{p_k^{t} \le n} (f(p_k^{t + 1}) + f(p_k^{t}) S(\lfloor\frac{n}{p_k^t}\rfloor, k + 1)))
S(n,j)=g(n,∣Prime∣)−i=1∑j−1f(pi)+k=j∑pk2≤nt=1∑pkt≤n(f(pkt+1)+f(pkt)S(⌊pktn⌋,k+1)))
前面的
g
(
n
,
j
)
−
∑
i
=
1
j
−
1
f
(
p
i
)
g(n, j) - \sum_{i = 1}^{j - 1} f(p_i)
g(n,j)−∑i=1j−1f(pi) 即为质数部分,注意要减去所有质数
d
(
d
<
p
j
)
d~(d < p_j)
d (d<pj) 的
f
(
d
)
f(d)
f(d) 的贡献;
枚举合数时,我们需要考虑所有最小质因子为 d ( d ≥ p j ) d~(d \ge p_j) d (d≥pj) 的数 k k k 对应的 f ( k ) f(k) f(k) 的贡献。由于质数我们在前面已经计算过,因此对于 f ( d t + 1 ) f(d^{t + 1}) f(dt+1) 这一项,我们从 t = 1 t=1 t=1 而非 t = 0 t = 0 t=0 开始枚举。而后面的合数部分,我们类似 g g g 的处理,利用积性函数的性质把 f ( d t ) f(d^t) f(dt) 这一项提出来,同时把 d t d^t dt 这一项消掉,再递归求解所有最小质因子大于 d d d 的 S S S。
最后 S ( n , 0 ) S(n, 0) S(n,0) 即为答案,不过注意要考虑一下 f ( 1 ) f(1) f(1) 的贡献。
说了那么多,拿洛谷的模板题讲解一下吧:
定义积性函数 f ( x ) f(x) f(x),当 p p p 为质数时有 f ( p k ) = p k ( p k − 1 ) f(p^k) = p^k(p^k - 1) f(pk)=pk(pk−1)。
给定
n
(
n
≤
1
0
10
)
n ~(n \le 10^{10})
n (n≤1010),求:
∑
i
=
1
n
f
(
i
)
\sum_{i = 1}^n f(i)
i=1∑nf(i)
对
1
0
9
+
7
10^9 + 7
109+7 取模。
题解:
- S t e p 1 Step~1 Step 1:
显然对于质数 p p p,有 f ( p ) = p ( p − 1 ) = p 2 − p f(p) = p(p - 1) = p^2 - p f(p)=p(p−1)=p2−p。可以发现,这正是上面提到的 “次数较少的简单多项式”。不看符号,它的每一项都是完全积性函数。
所以我们把 f ( p ) f(p) f(p) 拆成 p 2 p^2 p2 和 p p p 两项计算,最后相减即可。
我们再次回顾递推式:
g
(
n
,
j
)
=
g
(
n
,
j
−
1
)
−
h
(
p
j
)
(
g
(
⌊
n
p
j
⌋
,
j
−
1
)
−
∑
i
=
1
j
−
1
h
(
p
i
)
)
g(n, j) = g(n, j - 1) - h(p_j)(g(\lfloor\frac{n}{p_j}\rfloor,j - 1) - \sum_{i = 1}^{j - 1}h(p_i))
g(n,j)=g(n,j−1)−h(pj)(g(⌊pjn⌋,j−1)−i=1∑j−1h(pi))
可以发现两件事情:
- ⌊ n p j ⌋ \lfloor\frac{n}{p_j}\rfloor ⌊pjn⌋ 最多会有 n \sqrt{n} n 种取值,可以考虑整除分块预处理;
- n n n 以内的质因子个数也是 n \sqrt{n} n 级别的,所以说 ∑ i = 1 j − 1 h ( p i ) ) \sum_{i = 1}^{j - 1}h(p_i)) ∑i=1j−1h(pi)) 也可以预处理。
所以我们先考虑把所有的素数筛出来,同时维护后面的那一项 ∑ i = 1 j − 1 h ( p i ) ) \sum_{i = 1}^{j - 1}h(p_i)) ∑i=1j−1h(pi)):
inline void Sieve()
{
NotPrime[1] = true;
for(register int i = 2; i <= s; i++) //s = sqrt(n)
{
if(!NotPrime[i])
{
Prime[++len] = i;
sum1[len] = GetMOD(sum1[len - 1] + i) % MOD;
sum2[len] = GetMOD(sum2[len - 1] + 1LL * i * i % MOD) % MOD;
}
for(register int j = 1; j <= len && i * Prime[j] <= s; j++)
{
NotPrime[i * Prime[j]] = true;
if(i % Prime[j] == 0LL) break;
}
}
return;
}
然后我们整除分块,把对应的值存储下来编号,这样可以节省空间。同时我们预处理出 g ( 1 ∼ n , 0 ) g(1 \sim n, 0) g(1∼n,0):
for(register LL l = 1, r; l <= n; l = r + 1)
{
r = n / (n / l), val[++m] = n / l;
g1[m] = GetMOD(Prefix1(val[m]) - 1LL + MOD), g2[m] = GetMOD(Prefix2(val[m]) - 1LL + MOD); //注意要减去 f(1) 的贡献
if(val[m] <= s) pos1[val[m]] = m; else pos2[n / val[m]] = m;
}
接着我们考虑求解 g g g,这里可以发现后面的值只会由前面推导过来,所以可以考虑把数组滚动:
for(register int i = 1; i <= len; i++)
{
for(register int j = 1; j <= m && Prime[i] * Prime[i] <= val[j]; j++)
{
g1[j] = GetMOD(g1[j] - 1LL * Prime[i] * GetMOD(g1[GetPos(val[j] / Prime[i])] - sum1[i - 1] + MOD) % MOD + MOD);
g2[j] = GetMOD(g2[j] - 1LL * Prime[i] * Prime[i] % MOD * GetMOD(g2[GetPos(val[j] / Prime[i])] - sum2[i - 1] + MOD) % MOD + MOD);
}
}
完毕。
- S t e p 2 Step~2 Step 2:
接下来考虑求解 S S S。实际上没有那么麻烦,直接暴力枚举然后递归即可:
inline LL S(LL k, int p)
{
if(Prime[p] > k) return 0LL;
LL res = GetMOD(GetMOD(g2[GetPos(k)] - g1[GetPos(k)] + MOD) - GetMOD(sum2[p] - sum1[p] + MOD) + MOD);
for(register int i = p + 1; i <= len && Prime[i] * Prime[i] <= k; i++)
{
LL pw = Prime[i];
for(register int j = 1; pw <= k; pw = pw * Prime[i], j++) res = GetMOD(res + pw % MOD * GetMOD(pw % MOD - 1LL + MOD) % MOD * GetMOD(S(k / pw, i) + (j > 1)) % MOD);
}
return res;
}
贴上完整代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cassert>
#include<cmath>
#define MAXN 200005
using LL = long long;
using namespace std;
int len, m, pos1[MAXN], pos2[MAXN];
LL n, s, Prime[MAXN], sum1[MAXN], sum2[MAXN], val[MAXN], g1[MAXN], g2[MAXN];
bool NotPrime[MAXN];
const LL MOD = 1000000007LL, Inv2 = 500000004LL, Inv6 = 166666668LL;
inline LL GetMOD(LL k) { if(k >= MOD) k -= MOD; return k; }
inline LL Prefix1(LL k) { k %= MOD; return k * (k + 1LL) % MOD * Inv2 % MOD; }
inline LL Prefix2(LL k) { k %= MOD; return k * (k + 1LL) % MOD * GetMOD(k * 2LL % MOD + 1LL) % MOD * Inv6 % MOD; }
inline int GetPos(LL k) { if(k <= s) return pos1[k]; return pos2[n / k]; }
inline void Sieve()
{
NotPrime[1] = true;
for(register int i = 2; i <= s; i++)
{
if(!NotPrime[i])
{
Prime[++len] = i;
sum1[len] = GetMOD(sum1[len - 1] + i) % MOD;
sum2[len] = GetMOD(sum2[len - 1] + 1LL * i * i % MOD) % MOD;
}
for(register int j = 1; j <= len && i * Prime[j] <= s; j++)
{
NotPrime[i * Prime[j]] = true;
if(i % Prime[j] == 0LL) break;
}
}
return;
}
inline LL S(LL k, int p)
{
if(Prime[p] > k) return 0LL;
LL res = GetMOD(GetMOD(g2[GetPos(k)] - g1[GetPos(k)] + MOD) - GetMOD(sum2[p] - sum1[p] + MOD) + MOD);
for(register int i = p + 1; i <= len && Prime[i] * Prime[i] <= k; i++)
{
LL pw = Prime[i];
for(register int j = 1; pw <= k; pw = pw * Prime[i], j++) res = GetMOD(res + pw % MOD * GetMOD(pw % MOD - 1LL + MOD) % MOD * GetMOD(S(k / pw, i) + (j > 1)) % MOD);
}
return res;
}
int main()
{
#ifdef FILE
freopen("Input.in", "r", stdin);
#endif
scanf("%lld", &n), s = (LL)sqrt(n), Sieve();
for(register LL l = 1, r; l <= n; l = r + 1)
{
r = n / (n / l), val[++m] = n / l;
g1[m] = GetMOD(Prefix1(val[m]) - 1LL + MOD), g2[m] = GetMOD(Prefix2(val[m]) - 1LL + MOD);
if(val[m] <= s) pos1[val[m]] = m; else pos2[n / val[m]] = m;
}
for(register int i = 1; i <= len; i++)
{
for(register int j = 1; j <= m && Prime[i] * Prime[i] <= val[j]; j++)
{
g1[j] = GetMOD(g1[j] - 1LL * Prime[i] * GetMOD(g1[GetPos(val[j] / Prime[i])] - sum1[i - 1] + MOD) % MOD + MOD);
g2[j] = GetMOD(g2[j] - 1LL * Prime[i] * Prime[i] % MOD * GetMOD(g2[GetPos(val[j] / Prime[i])] - sum2[i - 1] + MOD) % MOD + MOD);
}
}
printf("%lld\n", GetMOD(S(n, 0) + 1LL));
return 0;
}
P o w e r f u l N u m b e r Powerful~Number Powerful Number
对正整数 k k k 唯一分解为 ∑ p i ∈ P r i m e p i α i \sum_{p_i\in Prime} p_i^{\alpha_i} ∑pi∈Primepiαi,如满足 ∀ p i ∈ P r i m e , α i ≥ 2 \forall p_i \in {Prime}, \alpha_i \ge 2 ∀pi∈Prime,αi≥2,则称其为 P o w e r f u l N u m b e r Powerful~Number Powerful Number。
每一个 P o w e r f u l N u m b e r Powerful~Number Powerful Number 都可以表示成 a 2 b 3 a^2 b^3 a2b3 的形式。由此我们也可以证明: 1 ∼ n 1 \sim n 1∼n 内 P o w e r f u l N u m b e r Powerful~Number Powerful Number 的数量在 O ( n ) O(\sqrt{n}) O(n) 级别。
它有什么用处呢?
假设我们要求积性函数 f f f 的前缀和,考虑构造一个前缀和很好求的函数 g g g,满足两函数在质数处值相等。
再设函数 h h h 使得 f = h ∗ g f = h * g f=h∗g, ∗ * ∗ 为狄利克雷卷积。
可以发现,当 d d d 为质数时,有 f ( d ) = h ( d ) + g ( d ) = g ( d ) f(d) = h(d) + g(d) = g(d) f(d)=h(d)+g(d)=g(d)。换句话说,当 d d d 不为 P o w e r f u l N u m b e r Powerful~Number Powerful Number 时, h ( d ) h(d) h(d) 取值为 0 0 0。
又有:
∑
i
=
1
n
f
(
i
)
=
∑
i
=
1
n
(
h
∗
g
)
(
i
)
=
∑
i
=
1
n
∑
d
∣
i
h
(
d
)
g
(
i
d
)
=
∑
d
=
1
n
h
(
d
)
∑
i
=
1
⌊
n
d
⌋
g
(
i
)
\begin{aligned} \sum_{i = 1}^n f(i) &= \sum_{i = 1}^n (h * g)(i) \\ &=\sum_{i = 1}^n\sum_{d | i} h(d)g(\frac{i}{d}) \\ &=\sum_{d = 1}^nh(d)\sum_{i = 1}^{\lfloor\frac{n}{d}\rfloor} g(i) \end{aligned}
i=1∑nf(i)=i=1∑n(h∗g)(i)=i=1∑nd∣i∑h(d)g(di)=d=1∑nh(d)i=1∑⌊dn⌋g(i)
由于后者前缀和很好求,因此可以考虑暴力找出
n
n
n 以内的
P
o
w
e
r
f
u
l
N
u
m
b
e
r
Powerful~Number
Powerful Number,直接暴力加就行。
非特殊函数的筛法
一般思路:
- 如果函数满⾜类积性,也就是满⾜对于所有的 a ⊥ b a \perp b a⊥b 都有 f ( a b ) = f ( a ) ⊗ f ( b ) f(ab) = f(a) \otimes f(b) f(ab)=f(a)⊗f(b)( ⊗ \otimes ⊗ 是⼀种运算符)时,可以套⽤积性函数的筛法解决;
- 如果函数和其约数密切相关,则试着先从素数整次幂⽅⾯推导,然后拓展到含多种素数的合数上。最后根据推导得到的式⼦进⾏计算。
2497



被折叠的 条评论
为什么被折叠?



