狄利克雷卷积

狄利克雷卷积

随处可见的东西

建议阅读 数论函数基础 章节,了解基本概念与先要知识


此处获取本节调试数据 / 代码包

全文 绝大多数 内容是对 [0] 中讲述的 粗略抄写胡乱加工


1. 定义与性质

数论函数 上定义,两个 数论函数 \(f, g\)狄利克雷卷积 为一个新的 数论函数,记作 \(f * g\)

\[h (n) = \sum _ {xy = n} f (x) g (y) = \sum _ {d \mid n} f (d) g \left (\dfrac n d \right ) \]

一般的(多项式)卷积式 \(h (n) = \sum _ {x + y = n} f (x) g (y)\),可以对比一下

上式可以简记为 \(h = f * g\),直接 按照定义式计算,即 枚举因数,可以做到 \(O (n \ln n)\) 的复杂度

inline int* Dirichlet (const int *F, const int *G, const int N) {
	static int H[MAXN];
	for (int i = 1; i <= N; ++ i)
		for (int j = i; j <= N; j += i)
			H[j] += F[i] * G[j / i];
	return H;
}

一些 狄利克雷卷积的性质 如下

  • 交换律

    \[f * g = g * f \]

    证明显然

  • 分配律

    \[(f + g) * h = f * h + g * h \]

    显然,\(\sum _ {d \mid n} (f + g) (d) = \sum _ {d \mid n} (f (d) + g (d))\)

  • 结合律

    \[(f * g) * h = f * (g * h) \]

    显然,均等于 \(\sum _ {xyz = n} f (x) g (y) h (z)\)

  • 单位元:\(\epsilon\)

    \[\epsilon * f = f \]

    容易发现,\(\epsilon (1) = 1, \epsilon (x) = 0 ~ (x > 1)\)

  • 逆元:\(f ^ {-1}\)

    \[f * f ^ {-1} = \epsilon \]

    满足上式的两函数 互为逆元

    积性函数 一定 有且仅有一个逆元

    证明

    \(g = f ^ {-1}\),设 \(f(1) \neq 0\)(积性函数一定满足),显然有 \(g (1) f (1) = 1\)

    \(f\)积性函数 时,就有 \(g (1) = f (1) = 1\),然后尝试 递推得到 \(g\)

    \[\begin {aligned} (f * g) (n) &= \epsilon (n) & n > 0 \\ \sum _ {d \mid n} { f (d) g \left ( \dfrac n d \right) } &= 0 \\ f (1) g (n) + \sum _ {d \mid n, d \neq 1} { f (d) g \left ( \dfrac n d \right) } &= 0 \\ g (n) &= \dfrac { - \sum _ {d \mid n, d \neq 1} { f (d) g \left ( \frac n d \right) } } {f (1)} \\ g (n) &= - \sum _ {d \mid n, d \neq 1} { f (d) g \left ( \dfrac n d \right) } \end {aligned} \]

    这同时证明了 积性函数 逆元的 存在性唯一性

    倒数第二个式子,其实我们可以推广出,\(f\) 可逆的 充要条件 实质上是 \(f (1) \neq 0\)

    也就是 无需保证是积性函数

  • \(f = g\) 的 充要条件 是 \(f * h = g * h\),其中 \(h(1) \neq 0\)

    两边同乘 \(h ^ {-1}\) 即可

  • 积性函数狄利克雷卷积 也是 积性函数

    设存在积性函数 \(f, g\),有 \(h = f * g\),设 \(a, b\) 满足 \(a \perp b\)

    \[\begin {aligned} h (ab) &= f (ab) * g (ab) \\ &= \sum _ {d \mid a, t \mid b} { f (dt) g \left ( \dfrac {ab} {dt} \right ) } \\ &= \sum _ {d \mid a} { \sum _ {t \mid b} { f (d) f (t) g \left ( \dfrac a d \right ) g \left ( \dfrac b t \right ) } } \\ &= \left ( \sum _ {d \mid a} { f (d) g \left ( \dfrac a d \right ) } \right ) \left ( \sum _ {t \mid b} { f (t) g \left ( \dfrac b t \right ) } \right ) \\ &= h (a) h (b) \end {aligned} \]

    即得证

  • 积性函数逆元 也是 积性函数

    设存在 积性函数 \(f\),其 逆元 \(f ^ {-1}\),下文保证 \(ab \neq 0\),即 \(\epsilon (a) \epsilon (b) = 0\)

    \[\begin {aligned} f ^ {-1} (ab) &= - \sum _ {d \mid ab, d \neq 1} { f (d) f ^ {-1} \left ( \dfrac {ab} d \right) } \\ &= - \sum _ {i \mid a, j \mid b, ij \neq 1} { f (i) f (j) f ^ {-1} \left ( \dfrac a i \right ) f ^ {-1} \left ( \dfrac b j \right ) } \\ &= - \sum _ {i \mid a, j \mid b} { f (i) f (j) f ^ {-1} \left ( \dfrac a i \right ) f ^ {-1} \left ( \dfrac b j \right ) } - (-f (1) f (1) f ^ {-1} (a) f ^ {-1} (b)) \\ &= f ^ {-1} (a) f ^ {-1} (b) - \sum _ {i \mid a} { f (i) g \left ( \dfrac a i \right ) } \sum _ {j \mid b} { f (j) g \left ( \dfrac b j \right ) } \\ &= f ^ {-1} (a) f ^ {-1} (b) - \epsilon (a) \epsilon (b) \\ &= f ^ {-1} (a) f ^ {-1} (b) \end {aligned} \]

    综合这两个性质,我们发现 两个积性函数 都是 积性函数,但 和差 不是


2. 线性狄利克雷卷积

\(O (n \ln n)\) 还是 太菜了,现在我们来 加速它,考虑 线性做法

如果 \(f, g\)积性函数,我们可以尝试 利用性质 进行一些优化

\[\begin {aligned} h(n) &= \begin {cases} 1 & n = 1 \\ \sum \limits _ {c = 0} ^ k f (p ^ c) g (p ^ {k - c}) & n = p ^ k, p \in \mathbb P \\ h (p ^ k) h (m) & n = p ^ k m ~ (m > 1, p \nmid m) \end {cases} \end {aligned} \]

第一部分简单的,而 第三部分 可以通过 线性筛 处理,\(p ^ k\) 就是 \(n\) 最小质因子的最高次幂

于是难点在于 快速求出第二部分,即 任意质数幂项 的 值

显然,若 \(f, g\)质数幂 处的取值 已经求出,我们需要 \(O (k)\) 的时间来计算 \(h\) 的值

\(f, g\)质数幂 处的取值也可以 线性筛筛出,或可能 \(O(1)\) 求得(否则就不好做了)

考虑估算 此时的复杂度,显然,每个 $ \le \sqrt [k] n$ 的 质数 \(p\) 会贡献 \(O(k)\) 的复杂度

\[\begin {aligned} T (n) &= \sum _ {k = 1} ^ {\log _ 2 n} { k \pi (\sqrt [k] n) } \\ &= \sum _ {k = 1} ^ {\log _ 2 n} { \dfrac { k \sqrt [k] n } { \ln \sqrt [k] n } } \\ &= \dfrac 1 {\ln n} \sum _ {k = 1} ^ {\log _ 2 n} { k ^ 2 \sqrt [k] n } \end {aligned} \]

首先,显然 \(\ln n\)\(\sum _ {x = 1} ^ {\log _ 2 n} 1 = \log _ 2 n\) 同阶

故若 \(k ^ 2 \sqrt [k] n\) 的上界 与 \(k\) 无关,则 \(T (n) = k ^ 2 \sqrt [k] n\)

显然,随着 \(k\) 增长,\(k ^ 2\) 单调递增\(\sqrt [k] n\) 单调递减,并显然 \(k ^ 2\) 增长速度远小于 \(\sqrt [k] n\) 减小速度

故我们认为,其极大值必在定义域端点处取到,验证 \(k = 1, k = \log _ 2 n\) 容易证明其小于 \(n\),故

\[\begin {aligned} T(n) &\le \dfrac 1 {\ln n} \sum _ {k = 1} ^ {\log _ 2 n} O (n) = O(n) \end {aligned} \]

故得证,用 线性筛两个质数幂处已知的积性函数狄利克雷卷积 可以做到 \(O(n)\) 复杂度

这也就是 线性筛 处提到的 \(O(k)\) 求出 质数幂 处值的方法


Luogu P6222 「P6156 简单题」加强版

推狮子,需要用一些据说是 经典套路 的东西,即 枚举 \(\gcd\),然后需要用 莫比乌斯反演*

* : 这个东西后面会 细讲,这里只先 放个式子,可以使用 [3] [4] 这些资料来深入学习

\[\begin {aligned} \sum _ {d \mid n} \mu (d) = [n = 1] \end {aligned} \]

还有一个更泛化的

\[\begin {aligned} f (n) = \sum _ {d \mid n} g (d) \Longrightarrow g (n) = \sum _ {d \mid n} \mu (d) f (\dfrac n d) \end {aligned} \]

然后启动!(后面有设 \(T = td\)

\[\begin {aligned} & \sum _ {i = 1} ^ n \sum _ {j = 1} ^ n (i + j) ^ k \mu ^ 2 (\gcd (i, j)) \gcd (i, j) \\ =& \sum _ {i = 1} ^ n \sum _ {j = 1} ^ n (i + j) ^ k \sum _ {d = 1} ^ n { [\gcd (i, j) = d] \mu ^ 2 (d) d } \\ =& \sum _ {d = 1} ^ n \mu ^ 2 (d) d \left ( \sum _ {i = 1} ^ n \sum _ {j = 1} ^ n (i + j) ^ k [\gcd (i, j) = d] \right ) \\ =& \sum _ {d = 1} ^ n \mu ^ 2 (d) d \left ( \sum _ {i = 1} ^ { \left \lfloor \frac n d \right \rfloor } { \sum _ {j = 1} ^ { \left \lfloor \frac n d \right \rfloor } { (id + jd) ^ k [\gcd (i, j) = 1] } } \right ) \\ =& \sum _ {d = 1} ^ n \mu ^ 2 (d) d \left ( \sum _ {i = 1} ^ { \left \lfloor \frac n d \right \rfloor } { \sum _ {j = 1} ^ { \left \lfloor \frac n d \right \rfloor } { d ^ k (i + j) ^ k [\gcd (i, j) = 1] } } \right ) \\ =& \sum _ {d = 1} ^ n \mu ^ 2 (d) d \left ( \sum _ {i = 1} ^ { \left \lfloor \frac n d \right \rfloor } { \sum _ {j = 1} ^ { \left \lfloor \frac n d \right \rfloor } { d ^ k (i + j) ^ k \sum _ {t \mid i, t \mid j} \mu (t) } } \right ) \\ =& \sum _ {d = 1} ^ n \mu ^ 2 (d) d ^ {k + 1} \sum _ {t = 1} ^ { \left \lfloor \frac n d \right \rfloor } \mu (t) \sum _ {i = 1} ^ { \left \lfloor \frac n {dt} \right \rfloor } { \sum _ {j = 1} ^ { \left \lfloor \frac n {dt} \right \rfloor } { (it + jt) ^ k } } \\ =& \sum _ {d = 1} ^ n \mu ^ 2 (d) d ^ {k + 1} \sum _ {t = 1} ^ { \left \lfloor \frac n d \right \rfloor } \mu (t) t ^ k \sum _ {i = 1} ^ { \left \lfloor \frac n {dt} \right \rfloor } { \sum _ {j = 1} ^ { \left \lfloor \frac n {dt} \right \rfloor } { (i + j) ^ k } } \\ =& \sum _ {d = 1} ^ n \mu ^ 2 (d) \sum _ {t = 1} ^ { \left \lfloor \frac n d \right \rfloor } \mu (t) (dt) ^ k d \sum _ {i = 1} ^ { \left \lfloor \frac n {dt} \right \rfloor } { \sum _ {j = 1} ^ { \left \lfloor \frac n {dt} \right \rfloor } { (i + j) ^ k } } \\ =& \sum _ {d = 1} ^ n \mu ^ 2 (d) \sum _ {t = 1} ^ { \left \lfloor \frac n d \right \rfloor } \mu (t) T ^ k d \sum _ {i = 1} ^ { \left \lfloor \frac n T \right \rfloor } { \sum _ {j = 1} ^ { \left \lfloor \frac n T \right \rfloor } { (i + j) ^ k } } \\ =& \sum _ {T = 1} ^ n { T ^ k \sum _ {d \mid T} { \mu ^ 2 (d) \mu \left ( \dfrac T d \right ) d } } \sum _ {i = 1} ^ { \left \lfloor \frac n T \right \rfloor } { \sum _ {j = 1} ^ { \left \lfloor \frac n T \right \rfloor } { (i + j) ^ k } } \end {aligned} \]

然后死了,考虑分成两部分

\[\begin {aligned} f (T) &= \sum _ {d \mid T} \mu ^ 2 (d) \mu \left ( t \right ) d \\ g (n) &= \sum _ {i = 1} ^ n \sum _ {j = 1} ^ n (i + j) ^ k \end {aligned} \]

\(f\) 函数显然是 积性函数,可以 线性筛 狄利克雷卷积 直接 预处理掉

对于 \(g\) 函数,我们继续往下推,考虑枚举 \(s = i + j\)省去一个参数

\[\begin {aligned} g (n) =& \sum _ {i = 1} ^ n \sum _ {j = 1} ^ n (i + j) ^ k \\ =& \sum _ {s = 2} ^ {2n} \sum _ {i = \max (s - n, 1)} ^ {\min (n, s - 1)} s ^ k \\ =& \sum _ {s = 2} ^ {2n} (\min (n, s - 1) - \max (s - n, 1) + 1) s ^ k \end {aligned} \]

对于不好处理的 \(\max\), \(\min\),我们直接 钦定结果,即讨论 \(n > s - 1\), \(n < s - 1\) 两种情况贡献

\[\begin {aligned} &= \sum _ {s = 2} ^ {n} { ((s - 1) - 1 + 1) } s ^ k + \sum _ {s = n + 1} ^ {2n} { (n - (s - n) + 1) } s ^ k \\ &= \sum _ {s = 2} ^ n (s - 1) s ^ k + \sum _ {s = n + 1} ^ {2n} { (2n - s + 1) s ^ k } \\ &= \sum _ {s = 2} ^ n { (s ^ {k + 1} - s ^ k) } + \sum _ {s = n + 1} ^ {2n} { ((2n + 1) s ^ k - s ^ {k + 1}) } \end {aligned} \]

丢到线性筛里一起处理就行了,$s ^ k $ 显然是 完全积性函数

#include <bits/stdc++.h>
import std;

const int MAXN = 100005;

using namespace std;

uint32_t T, N, K, Q;
uint32_t Cnt = 0;
uint32_t S1[MAXN], S2[MAXN], F[MAXN], P[MAXN];
bool Vis[MAXN];

inline uint32_t Qpow (uint32_t a, uint32_t b) {
	uint32_t Ret = 1;
	while (b) {
		if (b & 1) Ret = Ret * a;
		a = a * a, b >>= 1;
	}
	return Ret;
}

inline void Sieve () {
	uint32_t tmp1, tmp2;
	 
	S1[1] = F[1] = 1;
	
	for (uint32_t i = 2; i <= N; ++ i) {
		if (!Vis[i]) {
			P[++ Cnt] = i, S1[i] = Qpow (i, K);
			F[i] = i - 1;
		}
		for (uint32_t j = 1; P[j] * i <= N && j <= Cnt; ++ j) {
			Vis[tmp1 = P[j] * i] = 1;
			S1[tmp1] = S1[P[j]] * S1[i];
			if (i % P[j] == 0) {
				tmp2 = i / P[j]; // ATTENTION tmp2 = tmp1 / (P[j] ^ 2)
				if (tmp2 % P[j] == 0) F[tmp1] = 0;
				if (tmp2 % P[j] != 0) F[tmp1] = - P[j] * F[tmp2];
				break ;
			}
			F[tmp1] = F[P[j]] * F[i];
		}
	}
	
	for (uint32_t i = 1; i <= N; ++ i) F[i] = F[i - 1] + F[i] * S1[i];
	for (uint32_t i = 1; i <= N; ++ i) S2[i] = S2[i - 1] + S1[i] * i, S1[i] += S1[i - 1];
}

inline uint32_t G (const int x) {
	return (2 * x + 1) * (S1[x << 1] - S1[x]) - (S2[x << 1] - S2[x]) + (S2[x] - S2[1]) - (S1[x] - S1[1]);
}

inline void Solve () {
	uint32_t Ans = 0, L, R;
	
	cin >> Q;
	
	for (L = 1; L <= Q; L = R + 1) 
		R = Q / (Q / L), Ans += G (Q / L) * (F[R] - F[L - 1]);
	
	cout << Ans << '\n';
}

int main () {
	
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	cin >> T >> N >> K;
	
	N <<= 1, Sieve ();
	
	while (T --) Solve ();
	
	return 0;
}

EXP - 1 - Luogu P6156 简单题


3. 狄利克雷前缀和

使用了这些 [1] [2] 高维前缀和的笔记 辅助理解

对于 任意数论函数\(f\),其与 常数函数 \(1\)狄利克雷卷积,等价于对 \(f\)狄利克雷前缀和

\(g = f * 1 = \sum _ {d \mid n} f (d)\)

也就是计算 给定函数 在其 所有因数处 的取值和,这东西常常有 良好的性质

暴力做是 简单的,枚举每个数倍数即可,时间复杂度 \(O (n \ln n)\)其实并不慢

但是我们还有 更加快 并且 也比较简单的做法 值得学习

考虑有 \(n = \prod p_i ^ {c_i}, d = \prod p_i ^ {k _i}\),那么 \(d \mid n\) 等价于 \(\forall i, k_i \le c_i\)

这里我们设 乘式均有无穷项,即 \(c_i, k_i\) 可以为 \(0\),两边 \(p_i\) 等价

于是我们相当于对于这个 无穷项数列 \(c_i\) 做了类似 枚举子集 的操作,像 高维前缀和 的形式

于是根据 高维前缀和 的实现,我们考虑 枚举每一维 并关于该维做前缀和

同时由于 总状态数有限,我们又可以把这些东西压到一个 一维数组中转移

具体而言,我们枚举质数 \(p_i\),枚举其可能的倍数 \(k\)(使得 \(p_i k \le n\)

设答案函数 \(g (n)\),则我们每次将 \(g (p_ik)\) 加上 \(g (k)\) 这个贡献

理解一下,\(p_ik\) 唯一分解 后只比 \(k\) 多了一次 \(p_i\),即对应项 \(c_i\) 加上了 \(1\)

于是上述转移用高维前缀和的思路,就是把 每个质数看作一维,每次对这一维做前缀和

根据前面 埃式筛法 的结论,容易知道这样时间复杂度是 \(O (n \ln \ln n)\) 的,写法和埃式筛很像

Luogu P5495 【模板】Dirichlet 前缀和

#include <bits/stdc++.h>

const int MAXN = 20000005;

using namespace std;

uint32_t N, Seed, Cnt, Ans;
uint32_t P[MAXN >> 3], A[MAXN];
bool Vis[MAXN];

inline void Next () {
	Seed ^= Seed << 13, Seed ^= Seed >> 17, Seed ^= Seed << 5;
}

inline void Prime () {
	for (uint32_t i = 2; i <= N; ++ i) {
		if (!Vis[i]) P[++ Cnt] = i;
		for (uint32_t j = 1; j <= Cnt && P[j] * i <= N; ++ j) {
			Vis[i * P[j]] = 1;
			if (i % P[j] == 0) break ;
		}
	}
}

inline void Dirichlet () {
	for (uint32_t i = 1; i <= Cnt; ++ i)
		for (uint32_t j = 1; P[i] * j <= N; ++ j)
			A[P[i] * j] += A[j];
}

int main () {
	
	cin >> N >> Seed, Next ();
	
	for (uint32_t i = 1; i <= N; ++ i) A[i] = Seed, Next ();
	
	Prime (), Dirichlet ();
	
	for (uint32_t i = 1; i <= N; ++ i) Ans ^= A[i];
	
	cout << Ans << '\n';
	
	return 0;
}

4. 引用资料

[0] Number Theory —— H_W_Y

[1] 【数论】高维前缀和 笔记 —— C_liar

[2] 高维前缀和(SOSDP)—— 四糸智乃

[3] 莫比乌斯反演简要笔记 —— Sengxian

[4] 莫比乌斯反演 —— OI Wiki

posted @ 2024-07-22 20:00  FAKUMARER  阅读(7)  评论(0编辑  收藏  举报