狄利克雷卷积
狄利克雷卷积
随处可见的东西
建议阅读 数论函数基础 章节,了解基本概念与先要知识
全文 绝大多数 内容是对 [0] 中讲述的 粗略抄写 和 胡乱加工
1. 定义与性质
在 数论函数 上定义,两个 数论函数
一般的(多项式)卷积式
,可以对比一下
上式可以简记为
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;
}
一些 狄利克雷卷积的性质 如下
-
交换律
证明显然
-
分配律
显然,
-
结合律
显然,均等于
-
单位元:
容易发现,
-
逆元:
满足上式的两函数 互为逆元
积性函数 一定 有且仅有一个逆元
证明
设
,设 (积性函数一定满足),显然有当
为 积性函数 时,就有 ,然后尝试 递推得到这同时证明了 积性函数 逆元的 存在性 和 唯一性
从 倒数第二个式子,其实我们可以推广出,
可逆的 充要条件 实质上是也就是 无需保证是积性函数
-
的 充要条件 是 ,其中两边同乘
即可 -
积性函数 的 狄利克雷卷积 也是 积性函数
典
设存在积性函数
,有 ,设 满足即得证
-
积性函数 的 逆元 也是 积性函数
设存在 积性函数
,其 逆元 ,下文保证 ,即综合这两个性质,我们发现 两个积性函数 的 积 和 商 都是 积性函数,但 和差 不是
2. 线性狄利克雷卷积
如果
第一部分 是 简单的,而 第三部分 可以通过 线性筛 处理,
于是难点在于 快速求出第二部分,即 任意质数幂项 的 值
显然,若
在 质数幂 处的取值也可以 线性筛筛出,或可能 求得(否则就不好做了)
考虑估算 此时的复杂度,显然,每个
首先,显然
故若
显然,随着
故我们认为,其极大值必在定义域端点处取到,验证
故得证,用 线性筛 求 两个质数幂处已知的积性函数 的 狄利克雷卷积 可以做到
这也就是 线性筛 处提到的
求出 质数幂 处值的方法
Luogu P6222 「P6156 简单题」加强版
推狮子,需要用一些据说是 经典套路 的东西,即 枚举
* : 这个东西后面会 细讲,这里只先 放个式子,可以使用 [3] [4] 这些资料来深入学习
还有一个更泛化的
然后启动!(后面有设
然后死了,考虑分成两部分
对于
对于不好处理的
丢到线性筛里一起处理就行了,
#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;
}
3. 狄利克雷前缀和
对于 任意数论函数
即
也就是计算 给定函数 在其 所有因数处 的取值和,这东西常常有 良好的性质
暴力做是 简单的,枚举每个数倍数即可,时间复杂度
但是我们还有 更加快 并且 也比较简单的做法 值得学习
考虑有
这里我们设 乘式均有无穷项,即
可以为 ,两边 等价
于是我们相当于对于这个 无穷项数列
于是根据 高维前缀和 的实现,我们考虑 枚举每一维 并关于该维做前缀和
同时由于 总状态数有限,我们又可以把这些东西压到一个 一维数组中转移
具体而言,我们枚举质数
设答案函数
理解一下,
于是上述转移用高维前缀和的思路,就是把 每个质数看作一维,每次对这一维做前缀和
根据前面 埃式筛法 的结论,容易知道这样时间复杂度是
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具