积性函数学习入门笔记

积性函数是什么?

  • 如果当 \(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\)
posted @ 2021-06-26 15:32  init-神眷の樱花  阅读(134)  评论(0)    收藏  举报