狄利克雷卷积

狄利克雷卷积

随处可见的东西

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


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

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


1. 定义与性质

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

h(n)=xy=nf(x)g(y)=dnf(d)g(nd)

一般的(多项式)卷积式 h(n)=x+y=nf(x)g(y),可以对比一下

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

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;
}

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

  • 交换律

    fg=gf

    证明显然

  • 分配律

    (f+g)h=fh+gh

    显然,dn(f+g)(d)=dn(f(d)+g(d))

  • 结合律

    (fg)h=f(gh)

    显然,均等于 xyz=nf(x)g(y)h(z)

  • 单位元:ϵ

    ϵf=f

    容易发现,ϵ(1)=1,ϵ(x)=0 (x>1)

  • 逆元:f1

    ff1=ϵ

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

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

    证明

    g=f1,设 f(1)0(积性函数一定满足),显然有 g(1)f(1)=1

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

    (fg)(n)=ϵ(n)n>0dnf(d)g(nd)=0f(1)g(n)+dn,d1f(d)g(nd)=0g(n)=dn,d1f(d)g(nd)f(1)g(n)=dn,d1f(d)g(nd)

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

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

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

  • f=g 的 充要条件 是 fh=gh,其中 h(1)0

    两边同乘 h1 即可

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

    设存在积性函数 f,g,有 h=fg,设 a,b 满足 ab

    h(ab)=f(ab)g(ab)=da,tbf(dt)g(abdt)=datbf(d)f(t)g(ad)g(bt)=(daf(d)g(ad))(tbf(t)g(bt))=h(a)h(b)

    即得证

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

    设存在 积性函数 f,其 逆元 f1,下文保证 ab0,即 ϵ(a)ϵ(b)=0

    f1(ab)=dab,d1f(d)f1(abd)=ia,jb,ij1f(i)f(j)f1(ai)f1(bj)=ia,jbf(i)f(j)f1(ai)f1(bj)(f(1)f(1)f1(a)f1(b))=f1(a)f1(b)iaf(i)g(ai)jbf(j)g(bj)=f1(a)f1(b)ϵ(a)ϵ(b)=f1(a)f1(b)

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


2. 线性狄利克雷卷积

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

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

h(n)={1n=1c=0kf(pc)g(pkc)n=pk,pPh(pk)h(m)n=pkm (m>1,pm)

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

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

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

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

考虑估算 此时的复杂度,显然,每个 nk质数 p 会贡献 O(k) 的复杂度

T(n)=k=1log2nkπ(nk)=k=1log2nknklnnk=1lnnk=1log2nk2nk

首先,显然 lnnx=1log2n1=log2n 同阶

故若 k2nk 的上界 与 k 无关,则 T(n)=k2nk

显然,随着 k 增长,k2 单调递增nk 单调递减,并显然 k2 增长速度远小于 nk 减小速度

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

T(n)1lnnk=1log2nO(n)=O(n)

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

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


Luogu P6222 「P6156 简单题」加强版

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

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

dnμ(d)=[n=1]

还有一个更泛化的

f(n)=dng(d)g(n)=dnμ(d)f(nd)

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

i=1nj=1n(i+j)kμ2(gcd(i,j))gcd(i,j)=i=1nj=1n(i+j)kd=1n[gcd(i,j)=d]μ2(d)d=d=1nμ2(d)d(i=1nj=1n(i+j)k[gcd(i,j)=d])=d=1nμ2(d)d(i=1ndj=1nd(id+jd)k[gcd(i,j)=1])=d=1nμ2(d)d(i=1ndj=1nddk(i+j)k[gcd(i,j)=1])=d=1nμ2(d)d(i=1ndj=1nddk(i+j)kti,tjμ(t))=d=1nμ2(d)dk+1t=1ndμ(t)i=1ndtj=1ndt(it+jt)k=d=1nμ2(d)dk+1t=1ndμ(t)tki=1ndtj=1ndt(i+j)k=d=1nμ2(d)t=1ndμ(t)(dt)kdi=1ndtj=1ndt(i+j)k=d=1nμ2(d)t=1ndμ(t)Tkdi=1nTj=1nT(i+j)k=T=1nTkdTμ2(d)μ(Td)di=1nTj=1nT(i+j)k

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

f(T)=dTμ2(d)μ(t)dg(n)=i=1nj=1n(i+j)k

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

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

g(n)=i=1nj=1n(i+j)k=s=22ni=max(sn,1)min(n,s1)sk=s=22n(min(n,s1)max(sn,1)+1)sk

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

=s=2n((s1)1+1)sk+s=n+12n(n(sn)+1)sk=s=2n(s1)sk+s=n+12n(2ns+1)sk=s=2n(sk+1sk)+s=n+12n((2n+1)sksk+1)

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

#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=f1=dnf(d)

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

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

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

考虑有 n=pici,d=piki,那么 dn 等价于 i,kici

这里我们设 乘式均有无穷项,即 ci,ki 可以为 0,两边 pi 等价

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

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

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

具体而言,我们枚举质数 pi,枚举其可能的倍数 k(使得 pikn

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

理解一下,pik 唯一分解 后只比 k 多了一次 pi,即对应项 ci 加上了 1

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

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

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 @   FAKUMARER  阅读(47)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示