问题描述
求
∑
i
=
1
n
f
(
i
)
(
m
o
d
M
o
d
)
\sum_{i=1}^{n}f(i) \pmod{Mod}
i=1∑nf(i)(modMod)
其中
f
f
f 是一个积性函数。
算法解决
主要思路就是将积性函数分为三个部分来求
- 当 i i i 是质数。
- 当 i i i 是合数。
- 当 i = 1 i=1 i=1。
它的思想类似 DP
。
Min25 \text{Min25} Min25 筛的要求: f ( p ) f(p) f(p) 是一个关于 p p p 的项数较少的多项式((或能表示成若干项使得每一项都是完全积性函数); f ( p c ) f(p^{c}) f(pc) 可以快速求值。
step1 \text{step1} step1
求 g ( n ) = ∑ p ≤ n f ( p ) g(n)=\sum\limits_{p\leq n}f(p) g(n)=p≤n∑f(p)。
我们首先需要了解一下埃氏筛法(圈一个质数,划去它的倍数)。
先来考虑如何表示一个埃氏筛的状态。
那么用前 k k k 个质数来筛,最后没有被叉掉的数的最小质因子必然大于 p k p_k pk。
设 g ( n , k ) g(n,k) g(n,k) 表示 2 ∼ n 2 \sim n 2∼n 中的所有质数或最小质因子大于 p k p_k pk 的数的 f f f 值和。
怎么转移?
考虑埃氏筛的过程,选定一个质数后,直接从它的平方开始筛。
所以当 p k 2 > n p_k^2 > n pk2>n 时, g n , k = g n , k − 1 g_{n,k}=g_{n,k-1} gn,k=gn,k−1。( p k p_k pk 指第 k k k 个质数)。
否则我们就一定会再叉掉一些数,减掉一部分
f
f
f 值。
g
(
n
,
k
)
=
g
(
n
,
k
−
1
)
−
f
(
p
k
)
⋅
(
g
(
⌊
n
p
k
⌋
,
k
−
1
)
−
g
(
p
k
−
1
,
k
−
1
)
)
\large g(n,k)=g(n,k-1)-f(p_k)\cdot \left(g(\lfloor\frac{n}{p_k}\rfloor,k-1)-g(p_k-1,k-1)\right)
g(n,k)=g(n,k−1)−f(pk)⋅(g(⌊pkn⌋,k−1)−g(pk−1,k−1))
这里利用了
f
f
f 是积性函数的条件。
f ( p k ) ⋅ g ( ⌊ n p k ⌋ , k − 1 ) f(p_k)\cdot g_(\lfloor\frac{n}{p_k}\rfloor,k-1) f(pk)⋅g(⌊pkn⌋,k−1)。
理解:用前 k − 1 k-1 k−1 个质数来筛,把没筛掉的数,作为 p k p_k pk 的倍数(这个倍数一定会与前 k − 1 k-1 k−1 个质数互质且这个倍数 ⋅ p k < n \cdot p_k < n ⋅pk<n)来确定 p k p_k pk 需要筛哪些点。
由于前 k − 1 k-1 k−1 个质数也是剩下的数,所以还要加上 f ( p k ) ⋅ g p k − 1 , k − 1 f(p_k) \cdot g_{p_k-1,k-1} f(pk)⋅gpk−1,k−1(也就是 f ( p k ) ⋅ ∑ i = 1 k − 1 f ( p i ) f(p_k)\cdot \sum\limits_{i=1}^{k-1}f(p_i) f(pk)⋅i=1∑k−1f(pi),即 p k p_k pk 与前 k − 1 k-1 k−1 个质数的组合)。
step2 \text{step2} step2
求 s ( n ) = ∑ i ≤ n f ( i ) s(n)=\sum\limits_{i\leq n}f(i) s(n)=i≤n∑f(i)。
设 s ( n , k ) s(n,k) s(n,k) 表示 1 ∼ n 1 \sim n 1∼n 中最小质因子大于 p k p_k pk 的数的 f f f 值和。
那么有转移:
s
(
n
,
j
)
=
g
(
n
)
−
g
(
p
j
)
+
∑
j
<
k
and
p
k
≤
n
∑
1
≤
e
and
p
k
e
≤
n
f
(
p
k
e
)
(
s
(
⌊
n
p
k
e
⌋
,
k
)
+
[
e
≠
1
]
)
\large s(n,j)=g(n)-g(p_j)+\sum_{j<k \operatorname{and}p_k\leq \sqrt{n}}\sum_{1 \leq e \operatorname{and} p_k^e \leq n} f(p_k^e)\left(s\left(\lfloor\frac{n}{p_k^e}\rfloor,k\right) +[e \ne 1]\right)
s(n,j)=g(n)−g(pj)+j<kandpk≤n∑1≤eandpke≤n∑f(pke)(s(⌊pken⌋,k)+[e=1])
意义为:分质数,合数考虑贡献。
质数的贡献我们已经搞好了,为 g ( n ) − g ( p j ) g(n)-g(p_j) g(n)−g(pj),即 ∑ p ≤ n f ( p ) − ∑ i = 1 j − 1 f ( p i ) \sum\limits_{p\leq n}f(p)-\sum\limits_{i=1}^{j-1}f(p_i) p≤n∑f(p)−i=1∑j−1f(pi)。
合数的话,我们需要枚举最小质因子及其次数,即 k > j k>j k>j 的 p k p_k pk,再枚举次数 e e e。
由于是积性函数,我们直接将 f ( p k e ) f(p_k^e) f(pke) 提出。
值得注意的是,形如 p k , k > 1 p^k,k>1 pk,k>1 的数我们既没有在质数出枚举到,也没有在合数处提出 p k e p_k^e pke 后枚举到,所以需要加上个 [ e ≠ 1 ] [e \ne 1] [e=1]。
转移二:
设
s
(
n
,
j
)
=
∑
i
=
1
n
f
(
i
)
[
i
是
质
数
或
其
最
小
质
因
子
≥
p
j
]
s(n,j)=\sum_{i=1}^{n}f(i)[i 是质数或其最小质因子 \ge p_j]
s(n,j)=∑i=1nf(i)[i是质数或其最小质因子≥pj]。
s
(
n
,
j
)
=
s
(
n
,
j
+
1
)
+
∑
p
j
+
1
≤
n
∑
1
≤
e
and
p
j
+
1
e
≤
n
f
(
p
j
+
1
e
)
(
s
(
⌊
n
p
j
+
1
e
⌋
,
j
+
1
)
−
g
(
p
j
+
1
)
)
+
f
p
j
+
1
e
+
1
s(n,j)=s(n,j+1)+\sum_{p_{j+1} \leq \sqrt{n}}\sum_{1 \leq e \operatorname{and} p_{j+1}^{e}\leq n}f(p_{j+1}^{e})\left(s\left(\lfloor\frac{n}{p_{j+1}^{e}\rfloor},j+1\right)-g(p_{j+1}) \right)+f_{p_{j+1}^{e+1}}
s(n,j)=s(n,j+1)+pj+1≤n∑1≤eandpj+1e≤n∑f(pj+1e)(s(⌊pj+1e⌋n,j+1)−g(pj+1))+fpj+1e+1
这种方法虽然实际效率劣于方法一,但这种方法不仅算出了
s
(
n
)
s(n)
s(n),也顺带计算出了所有
s
(
⌊
n
x
⌋
)
s(\lfloor\frac{n}{x}\rfloor)
s(⌊xn⌋),这是方法一做不到的。
step3 \text{step3} step3
最后加上 f ( 1 ) f(1) f(1) 即可。
总时间复杂度约为 O ( n 0.75 log n ) \mathcal O(\frac{n^{0.75}}{\log n}) O(lognn0.75)。
实现细节和代码
P5325 【模板】Min_25筛
定义积性函数 f ( x ) f(x) f(x),且 f ( p k ) = p k ( p k − 1 ) f(p^k)=p^k(p^k-1) f(pk)=pk(pk−1)( p p p 是一个质数),求
∑ i = 1 n f ( i ) \sum_{i=1}^n f(i) i=1∑nf(i)
对 1 0 9 + 7 10^9+7 109+7 取模。1 ≤ n ≤ 1 0 10 1 \leq n \leq 10^{10} 1≤n≤1010。
首先,由题意有, f ( p ) = p 2 − p f(p)=p^2-p f(p)=p2−p,维护一下 g 1 = p g_1=p g1=p 和 g 2 = p 2 g_2=p^2 g2=p2 的前缀和然后用 Min25 \text{Min25} Min25 筛即可。
细节:
- 在求 g g g 的时候空间太大了,需要优化,因为 g g g 都是由 n k \frac{n}{k} kn 处推来的,众所周知 n k \frac{n}{k} kn 的取值只有 2 n 2\sqrt{n} 2n 种情况,让 i d 1 k id1_k id1k 记录 k ≤ n k\le\sqrt n k≤n, i d 2 k id2_k id2k 记录 n k ≤ n \frac{n}{k} \le \sqrt n kn≤n,所以可以只枚举这 2 n 2\sqrt n 2n 种情况。
- 拆成的 g g g 必须保证是完全积性函数,不然不可递推。
#include <bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &t)
{
t = 0;
char c = getchar();
int f = 1;
while (c < '0' || c > '9')
{
if (c == '-')
f = -f;
c = getchar();
}
while (c >= '0' && c <= '9')
{
t = (t << 3) + (t << 1) + c - '0';
c = getchar();
}
t *= f;
}
template <typename T, typename... Args>
inline void read(T &t, Args &...args)
{
read(t);
read(args...);
}
template <typename T>
inline void write(T x)
{
if (x < 0)
{
x = -x;
putchar('-');
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
#define ll long long
const int _ = 2e5 + 7;
const int mod = 1e9 + 7;
bool v[_];
ll tot, p[_], n, sq, w[_], m, sum1[_], sum2[_], g1[_], g2[_], id1[_], id2[_];
// id1 id2 如文中所示 w[k] 为第 k 个需要处理的数
// 文中的 g 为 g2 - g1, g1 : f(k) = k, g2: f(k) = k ^ 2
// sum1 : 质数前缀和 ; sum2: 质数平方前缀和
void pre() // 线性筛
{
for (int i = 2; i <= sq; ++i)
{
if (!v[i])
{
p[++tot] = i;
sum1[tot] = (sum1[tot - 1] + p[tot]) % mod;
sum2[tot] = (sum2[tot - 1] + p[tot] * p[tot]) % mod;
}
for (int j = 1; p[j] * i <= sq && j <= tot; j++)
{
v[p[j] * i] = 1;
if (i % p[j] == 0)
break;
}
}
}
ll getid(ll x)
{
if (x <= sq)
return id1[x];
else
return id2[n / x];
}
ll f1(ll x) // 1 ~ x 前缀和
{
x %= mod;
return x * (x + 1) / 2 % mod;
}
ll f2(ll x) // 1 ~ x 前缀平方和
{
x %= mod;
return x * (x + 1) % mod * (2 * x % mod + 1) % mod * 166666668 % mod;
}
ll S(ll x, int j)
{
if (p[j] > x)
return 0;
ll Ans = ((g2[getid(x)] - g1[getid(x)] + mod) % mod - (sum2[j] - sum1[j] + mod) % mod + mod) % mod;
for (int i = j + 1; i <= tot && p[i] * p[i] <= x; ++i)
for (ll e = 1, sp = p[i]; sp <= x; sp *= p[i], ++e)
Ans = (Ans + sp % mod * (sp % mod - 1) % mod * (S(x / sp, i) + (e > 1)) % mod) % mod;
return Ans;
}
signed main()
{
read(n);
sq = sqrt(n);
pre();
for (ll l = 1, r; l <= n; l = r + 1)
{
r = (n / (n / l)), w[++m] = n / l;
g1[m] = f1(w[m]) - 1, g2[m] = f2(w[m]) - 1; // 处理 g(?, 0)
if (w[m] <= sq)
id1[w[m]] = m;
else
id2[n / w[m]] = m;
}
for (int i = 1; i <= tot; ++i) // dp 处理 g 函数
{
for (int j = 1; j <= m && p[i] * p[i] <= w[j]; ++j)
{
g1[j] = (g1[j] - p[i] * (g1[getid(w[j] / p[i])] - sum1[i - 1]) % mod + mod) % mod;
g2[j] = (g2[j] - p[i] * p[i] % mod * (g2[getid(w[j] / p[i])] - sum2[i - 1]) % mod + mod) % mod;
}
}
write((S(n, 0) + 1ll) % mod), putchar('\n');
return 0;
}
例题
P4213 【模板】杜教筛(Sum)
给定一个正整数 n n n,求
a n s 1 = ∑ i = 1 n φ ( i ) a n s 2 = ∑ i = 1 n μ ( i ) ans1=\sum_{i=1}^{n}\varphi(i)\\ ans2=\sum_{i=1}^{n}\mu(i) ans1=i=1∑nφ(i)ans2=i=1∑nμ(i)
先看 φ \varphi φ,由 φ ( p ) = p − 1 \varphi(p)=p-1 φ(p)=p−1,我们可以维护 p p p 和 1 1 1 的前缀和。
另外,由 φ ( p c ) = p c − 1 ( p − 1 ) \varphi(p^c)=p^{c-1}(p-1) φ(pc)=pc−1(p−1),可知,质数的整次幂的函数值也很好求。
然后是 μ \mu μ,显然 μ ( p ) = − 1 \mu(p)=-1 μ(p)=−1,我们可以维护 1 1 1 的前缀和,然后取反。
另外, μ ( p k ) = 0 ( k > 1 ) \mu(p^k)=0(k>1) μ(pk)=0(k>1),因此求 s s s 时不需要枚举最小质因子的次数。
loj 6235 区间素数个数
求 1 ∼ n 1 \sim n 1∼n 之间素数个数。
2 ≤ n ≤ 1 0 11 2 \leq n \leq 10^{11} 2≤n≤1011。
求质数个数,联想到 Min25 \text{Min25} Min25 筛的第一步。相当于求 f ( p ) = 1 f(p)=1 f(p)=1 这个函数在质数处取值的前缀和。
loj 6181 某个套路求和题
记
f ( n ) = ∏ d ∣ n μ ( d ) f(n)=\prod_{d|n}\mu(d) f(n)=d∣n∏μ(d)
求
∑ i = 1 n f ( i ) ( m o d 998244353 ) \sum_{i=1}^{n}f(i) \pmod{998244353} i=1∑nf(i)(mod998244353)
1 ≤ n ≤ 1 0 10 1 \leq n \leq 10^{10} 1≤n≤1010。
易得
a
n
s
=
∑
i
=
1
n
μ
(
i
)
2
−
∑
i
=
1
n
2
×
[
i
∈
primes
]
ans=\sum_{i=1}^{n}\mu(i)^2-\sum_{i=1}^{n}2\times [i \in \text{primes}]
ans=i=1∑nμ(i)2−i=1∑n2×[i∈primes]
套个
Min25
\text{Min25}
Min25 筛即可。
loj 6053 简单的函数
求积性函数 f ( p c ) = p ⊕ c , p ∈ primes f(p^c)=p\oplus c,p\in \text{primes} f(pc)=p⊕c,p∈primes 的前缀和 ( m o d 1 0 9 + 7 ) \pmod{10^9+7} (mod109+7)。
1 ≤ n ≤ 1 0 10 1 \leq n \leq 10^{10} 1≤n≤1010。
易得
f
(
p
)
=
{
p
−
1
p
≠
2
p
+
1
p
=
2
f(p)=\begin{cases}p-1 & p \ne2 \\p+1& p=2\end{cases}
f(p)={p−1p+1p=2p=2
我们可以先把
f
(
p
)
f(p)
f(p) 当成
p
−
1
p-1
p−1,然后维护
p
p
p 和
1
1
1 的前缀和,最后特判即可。
注意,我们应当保证拆成的若干个函数是完全积性函数。
SP34096 DIVCNTK - Counting Divisors (general)
求 ∑ i = 1 n d ( i k ) ( m o d 2 64 ) \sum\limits_{i=1}^{n}d(i^k) \pmod{2^{64}} i=1∑nd(ik)(mod264)。
其中 d ( i ) d(i) d(i) 表示 i i i 的约数个数。
多测。
设 f ( x ) = d ( x k ) f(x)=d(x^k) f(x)=d(xk)。
则 f f f 满足一下几条性质:
- f ( 1 ) = 1 f(1)=1 f(1)=1。
- f ( p ) = k + 1 , p ∈ primes f(p)=k+1,p\in \text{primes} f(p)=k+1,p∈primes。
- f ( p c ) = k c + 1 f(p^c)=kc+1 f(pc)=kc+1。
- f ( a b ) = f ( a ) × f ( b ) , gcd ( a , b ) = 1 f(ab)=f(a)\times f(b),\gcd(a,b)=1 f(ab)=f(a)×f(b),gcd(a,b)=1。
套个 Min25 \text{Min25} Min25 筛即可。
[CODE](云剪贴板 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))
多倍经验(修改一下 k k k 的值即可)。
SP20173 DIVCNT2 - Counting Divisors (square)
SP20174 DIVCNT3 - Counting Divisors (cube)
总结
用 Min25 \text{Min25} Min25 做题主要是要想清楚两个问题:
- 该函数在质数处的取值怎么求和?
- 质数的次幂处的取值 f ( p c ) f(p^c) f(pc) 能否快速求?