积性函数学习入门笔记
积性函数是什么?
- 如果当 \(a,b\) 互质时,有 \(f(ab) = f(a) * f(b)\),那么称函数 \(f\) 为积性函数。
- 如果当 \(a,b\) 不互质的时候,也有这个性质, 那么我们称函数 \(f\) 为完全积性函数。
常用的积性函数
- \(\varphi(n)\):欧拉函数,\(1\) ~ \(n\) 中与 \(n\) 的 \(gcd = 1\) 的数的个数, 即 \(\sum_{i = 1} ^ {n} [gcd(i, n) == 1]\)。
- \(\mu(n)\):莫比乌斯函数,\(\mu(n) = \left\{ \begin{array}{lcl} 1, &n = 1 \\ (-1)^k, &n = p_{1} ^ {c_1} * p_{2} ^ {c_2} * … * p_{k} ^ {c_k} \\ 0, &else \\ \end{array} \right\}\)
- \(id(n)\): 恒等函数, \(id(n) = n\)。
- \(\epsilon(n)\):单位函数,\(\epsilon(n) = [n == 1]\)
- \(\sigma_k(n)\):除数函数,\(\sigma_k(n) = \sum_{d|n} d ^ k\),其中 \(\sigma_0(n)\) 表示 \(n\) 的约数个数,常记为 \(d(n)\),\(\sigma_1(n)\) 表示 \(n\) 的约数和。
- \(I(n)\):元函数,\(I(n) = 1\)
欧拉函数的应用
欧拉函数的性质:
- 设 \(p\) 为质数,若 \(p | n\) 且 \(p ^ 2 | n\),则 \(\varphi(n) = \varphi(n / p) * p\)。
- 设 \(p\) 为质数,若 \(p | n\) 但 \(p ^ 2 \nmid n\)$,则 \(\varphi(n) = \varphi(n / p) * (p - 1)\)。
- \(\sum_{d|n}\varphi(d) = n\)(\(\varphi(n) * I = id\))。
设 \(f(n) = \sum_{d|n}\varphi(d)\)。
利用 \(\varphi\) 是积性函数的性质,当 \(n, m\) 互质时, \(f(nm) = \sum_{d|nm}\varphi(d) = (\sum_{d|n}\varphi(d)) * (\sum_{d|m}\varphi(d)) = f(n) * (m)\),那么 \(f(n)\) 也是积性函数。
对于质数 \(p\),有 \(f(p^k) = \sum_{d|p^k}\varphi(d)\),那么能整除 \(p ^ k\) 的数就是 \(1, p, p ^ 2 , … ,p ^ k\)。
那么 \(f(p ^ k) = \varphi(1) + \varphi(p) + … + \varphi(p ^ k)\)。
根据上述的欧拉函数的第一个性质,可以得到,除了 \(1\) 以外的数,构成了一个等比数列,\((p - 1) * p ^ i\),那么对等比数列求和后,再加上前面的一个 \(1\),就得到了,\(f(p ^ k) = p ^ k\)。
对每一个数质因数分解后,利用积性函数的性质,就得到了 \(f(n) = f(p_{1} ^ {c_1}) * … * f(p_{m} ^ {c_m}) = p_{1} ^ {c_1} * … * p_{m} ^ {c_m} = n\)。
GCD SUM
P2398 GCD SUM
题目大意:
求 \(\sum_{i = 1} ^ {n} \sum_{j = 1} ^ {n} gcd(i,j)\)
反演入门题。
利用上述欧拉函数的性质 \(3\),可以得到 \(gcd(i,j) = \sum_{d \mid gcd(i,j)}\varphi(d)\)。
所以上柿就被反演为 \(\sum_{i = 1} ^ {n} \sum_{j = 1} ^ {n}\sum_{d \mid gcd(i,j)}\varphi(d)\)。
将内层的 \(d\) 拿到外层来枚举,得到 \(ans = \sum_{d = 1} ^ {n} \sum_{i = 1} ^ {\lfloor \frac{n}{d} \rfloor} \sum_{j = 1} ^ {\lfloor \frac{n}{d} \rfloor} 1\)。
显然后面的等于 \(\lfloor \frac{n}{d} \rfloor * \lfloor \frac{n}{d} \rfloor\)。
所以 \(ans = \sum_{d = 1} ^ {n} \varphi(d) * \lfloor \frac{n}{d} \rfloor * \lfloor \frac{n}{d} \rfloor\)。
化成这样的柿子之后,我们就能运用整除分块求解问题。
整除分块
可以发现,对于 \(1\leq i \leq n\),显然有一段 \(i\),它们的 \(\lfloor \frac{i}{d} \rfloor\) 值相等。
所以我们可以把前面的 \(\varphi\) 看成是系数,合并同类项。
然后就能一段一段的求出答案。
因为 \(\lfloor \frac{n}{d} \rfloor\) 的值有 \(\sqrt{n}\) 种,所以这样的块就有 \(\sqrt n\) 个,所以我们一共要计算 \(\sqrt n\) 次,时间复杂度就是 \(O(\sqrt n)\)。
预处理
根据上述欧拉函数的性质 \(1, 2\)。
我们可以在线性筛的过程中将 \(\varphi\) 的值递推出来。
具体实现请参见代码。
GCD SUM 代码
#include<cstdio>
#include<cstring>
#define LL long long
const int N = 1e5 + 5;
int Prime[N + 5], m, n; bool notPrime[N + 5];
LL phi[N + 5], ans = 0;
void euler() {
memset(notPrime, false, sizeof(notPrime));
m = 0; phi[1] = 1;
for(int i = 2; i <= N; i++) {
if(!notPrime[i]) {
Prime[++m] = i;
phi[i] = i - 1;
}
for(int j = 1; j <= m && 1ll * i * Prime[j] <= N; j++) {
notPrime[i * Prime[j]] = true;
if(i % Prime[j] == 0) {
phi[i * Prime[j]] = phi[i] * Prime[j];
break;
}
phi[i * Prime[j]] = phi[i] * phi[Prime[j]];
}
}
for(int i = 1; i <= N; i++)
phi[i] += phi[i - 1];
}
int main() {
euler();
scanf("%d", &n);
for(int l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
ans += (phi[r] - phi[l - 1]) * (n / l) * (n / l);
}
printf("%lld\n", ans);
return 0;
}
推荐练习
莫比乌斯函数的应用
莫比乌斯函数的性质:
- \(\sum_{d | n}\mu(d) = \epsilon(n)\)。
根据 \(\mu\) 是容斥系数的性质即可证明。
ZAP-Queries
ZAP-Queries
题目大意:求 \(\sum_{i = 1} ^ {a} \sum_{j = 1} ^ {b} [gcd(i,j) == d]\)。
这个柿子等价于 \(\sum_{i = 1} ^ {\lfloor \frac{a}{d} \rfloor} \sum_{j = 1} ^ {\lfloor \frac{b}{d} \rfloor} [gcd(i,j) == 1]\)。
那么 \([gcd(i,j) == 1]\) 就可以反演为 \(\sum_{d | n}\mu(d)\),由上述性质可得,只有当 \(gcd(i,j) == 1\) 时,\(\sum_{d | n}\mu(d)\) 才为 \(1\),符合条件。
那么原柿子就可化为 \(\sum_{i = 1} ^ {\lfloor \frac{a}{d} \rfloor} \sum_{j = 1} ^ {\lfloor \frac{b}{d} \rfloor} \sum_{k | n}\mu(k)\)。
按照上文的套路,就有 \(ans = \sum_{k = 1} ^ {n} \mu(k) * \lfloor \frac{a}{dk} \rfloor * \lfloor \frac{b}{dk} \rfloor\)。
那么我们就能利用整除分块求解了。
两个上界的整除分块
\(r\) 的取值方法有所变化。
\(r = min(a / (a / l), b / (b / l))\)。
因为我们要同时满足 \(a,b\)。
预处理
筛到质数令 \(mu = -1\),筛出的数能被 \(p ^ 2\) 整除的数令 \(mu = 0\),不能被整除的数令 \(mu[i * p] = -mu[i]\)。
详情请参见代码。
代码
和下面的有所的同,下面那个是以前学的时候写的,现在喜欢这样写:
void euler() {
memset(notPrime, false, sizeof(notPrime));
m = 0; mu[1] = 1;
for(int i = 2; i <= N; i++) {
if(!notPrime[i]) {
Prime[++m] = i;
mu[i] = -1;
}
for(int j = 1; j <= m && 1ll * i * Prime[j] <= N; j++) {
notPrime[i * Prime[j]] = true;
if(i % Prime[j] == 0) {
mu[i * Prime[j]] = 0;
break;
}
mu[i * Prime[j]] = -mu[i];
}
}
for(int i = 1; i <= N; i++)
mu[i] += mu[i - 1];
}
ZAP-Queries 代码
#include<cstdio>
#include<algorithm>
const int N = 1e5 + 5;
int prime[N],mu[N],pre[N],t,num = 0;
bool notprime[N];
void init()
{
mu[1] = 1;
for(int i = 2;i <= N;i++)
{
if(!notprime[i]) prime[++num] = i,mu[i] = -1;
for(int j = 1;j <= num && i * prime[j] <= N;j++)
{
notprime[i * prime[j]] = true;
if(i % prime[j] == 0){mu[i * prime[j]] = 0;break;}
mu[i * prime[j]] = mu[i] * mu[prime[j]];
}
}
for(int i = 1;i <= N;i++)
pre[i] = pre[i - 1] + mu[i];
}
int solve(int a,int b)
{
int ans = 0;
for(int i = 1,j;i <= a;i = j + 1)
{
j = std::min(a / (a / i),b / (b / i));
ans += (pre[j] - pre[i - 1]) * (a / i) * (b / i);
}
return ans;
}
inline int read()
{
int x = 0,flag = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') flag = -1;ch = getchar();}
while(ch >='0' && ch <='9'){x = (x << 3) + (x << 1) + ch - 48;ch = getchar();}
return x * flag;
}
int main()
{
init();
t = read();
for(int i = 1;i <= t;i++)
{
//int a = read(),b = read(),c = read(),d = read(),k = read();
int b = read(),d = read(),k = read();
if(b > d) std::swap(b,d);
printf("%d\n",solve(b / k,d / k));
}
return 0;
}
推荐练习
[国家集训队]Crash的数字表格 / JZPTAB
[CQOI2015]选数
数表
需要注意的地方
- 初始化不要忘了 \(\varphi_1 = \mu_1 = 1\)。
- 求前缀和要等筛完了再求。
- 注意筛数的数组不要越界了。
- 数据范围较大的时候在 \(Prime[j] * i <= N\) 处要强转 \(long long\)。

浙公网安备 33010602011771号