【学习笔记】狄利克雷卷积

这篇博客探讨了数学中函数卷积的概念及其性质,并通过两个实例展示了如何在编程中实现和应用这些概念。第一个例子涉及完全积性函数的卷积运算,第二个例子介绍了递推关系`fr(n)`的性质及其与积性函数的关系。博主通过归纳法证明了`fr(n)`是一个积性函数,并提出了一种预处理和快速计算的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本形式

f,gf,gf,g 函数卷起来写作 f∗g(n)f * g (n)fg(n),等于 ∑d∣nf(d)g(nd)\sum_{d \mid n} f (d) g (\frac{n}{d})dnf(d)g(dn)

性质:

  • f,gf, gf,g 都为(完全)积性函数,那么 f∗gf * gfg 也为(完全)积性函数。
  • f∗g=g∗ff * g = g * ffg=gf (交换律)
  • f∗g∗q=f∗(g∗q)f * g * q = f * (g * q)fgq=f(gq) (结合律)

例题

一、Clarke and mathClarke \ and \ mathClarke and math

gi=∑i1∣i∑i2∣i1∑i3∣i2⋯∑ik∣ik−1fik=∑i1∣i∑i2∣i1∑i3∣i2⋯1(nik−1)∑ik∣ik−1fik⋅1(iik)=1k∗f(i)g0(i)=igk(i)=∑d∣igk−1(d)∗f(nd) \begin{aligned} g_i &= \sum_{i_1∣i}\sum_{i_2∣i1}\sum_{i_3∣i2}⋯\sum_{i_k∣i_{k−1}}f_{i_k} \\ &= \sum_{i_1∣i}\sum_{i_2∣i_1}\sum_{i_3∣i_2}⋯ 1(\frac{n}{i_{k - 1}}) \sum_{i_k∣i_{k−1}} f_{i_k} \cdot 1(\frac{i}{i_k}) \\ &= 1 ^ k * f (i) \\ g_0 (i) &= i \\ g_k (i) &= \sum_{d \mid i} g_{k - 1} (d) * f (\frac{n}{d}) \end{aligned} gig0(i)gk(i)=i1ii2i1i3i2ikik1fik=i1ii2i1i3i21(ik1n)ikik1fik1(iki)=1kf(i)=i=digk1(d)f(dn)

//author : LH ——Who just can eat S??t
//worship WJC ——Who can f??k tourist up and down and loves 周歆恬
//worship YJX ——Who can f??k WJC up and down
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib> 
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)

template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
    x = 0; T f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar ();
    }
    x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
    read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
    if (x == 0) { putchar ('0'); return; }
    if (x < 0) { putchar ('-'); x = -x; }
    int poi = 0;
    while (x) {
        For_Print[++poi] = x % 10 + '0';
        x /= 10;
    }
    while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
    write (x); putchar (ch);
}

const LL Mod = 1e9 + 7;

LL square (LL x) { return (x * x) % Mod; }
void DEL (LL &x, LL y) { ((x -= y) < 0) && (x += Mod); }
void ADD (LL &x, LL y) { ((x += y) >= Mod) && (x -= Mod); }

const int Maxn = 1e5;

int t, n, k;

vector <int> d[Maxn + 5];
void Euler () {
    rep (i, 1, Maxn)
        for (int j = i; j <= Maxn; j += i)
            d[j].push_back (i);
}

struct Fold {
    int n;
    LL f[Maxn + 5];

    Fold () { n = 0; memset (f, 0, sizeof f); f[1] = 1; }
};
Fold operator * (Fold x, Fold y) {
    Fold res; res.n = x.n; res.f[1] = 0;
    rep (i, 1, res.n)
        for (auto j : d[i])
            ADD (res.f[i], x.f[j] * y.f[i / j] % Mod);
    return res;
}
Fold quick_pow (Fold x, int y) {
    Fold res; res.n = x.n;
    while (y) {
        if (y & 1) res = res * x;
        x = x * x; y >>= 1;
    }
    return res;
}

int main () {
	// freopen ("D:\\lihan\\1.in", "r", stdin);
	// freopen ("D:\\lihan\\1.out", "w", stdout);

    Euler ();

    read (t);
    while (t--) {
        read (n, k);
        Fold f, tmp; f.n = tmp.n = n;
        rep (i, 1, n) read (f.f[i]);
        rep (i, 1, n) tmp.f[i] = 1;

        f = f * quick_pow (tmp, k);
        rep (i, 1, n) {
            print (f.f[i], ' ');
        }
        putchar ('\n');
    }
    return 0;
}

二、Bash Plays with FunctionsBash \ Plays \ with \ FunctionsBash Plays with Functions

fr(n)=∑u⋅v=nfr−1u+fr−1v2=∑u∣nfr−1u=∑u∣nfr−1u⋅1(nu) \begin{aligned} f_r(n) &= \sum_{u \cdot v = n} \frac{f_{r - 1} u + f_{r - 1} v}{2} \\ &= \sum_{u \mid n} f_{r - 1} u \\ &= \sum_{u \mid n} f_{r - 1} u \cdot 1 (\frac{n}{u}) \end{aligned} fr(n)=uv=n2fr1u+fr1v=unfr1u=unfr1u1(un)

证明 fr(n)f_r(n)fr(n) 是一个积性函数。

归纳法,假设 fr−1(n)f_{r - 1}(n)fr1(n) 是一个积性函数。

则:

∵fr−1(n),1(n)\because f_{r - 1}(n),1 (n)fr1(n),1(n) 是积性函数。
∴fr(n)\therefore f_r (n)fr(n) 是积性函数。

又因为 f0(n)=2k(n=∏ikpiqi)f_0(n) = 2^k(n = \prod_{i}^{k} p_i^{q_i})f0(n)=2k(n=ikpiqi) 是积性函数。

得证。

fr(pk)=∑i=0kfr−1(pi)f_r(p^k) = \sum_{i = 0}^k f_{r - 1}(p^i)fr(pk)=i=0kfr1(pi)
f0(pk)=2f_0(p^k) = 2f0(pk)=2

我们观察到一个性质: fr(pk)f_r(p^k)fr(pk) 的取值只与 k,rk,rk,r 有关。

所以我们可以先跑一遍,预处理出 r=0r = 0r=0 ~ 1e61e61e6k=0k = 0k=0 ~ log2(1e6)log_2 (1e6)log2(1e6) 时,fr(pk)f_r (p^k)fr(pk) 的取值。

然后求 fr(n)f_r(n)fr(n) 时就质因数分解,乘起来就好了。

//author : LH ——Who just can eat S??t
//worship WJC ——Who can f??k tourist up and down and loves 周歆恬
//worship YJX ——Who can f??k WJC up and down
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib> 
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)

template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
    x = 0; T f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar ();
    }
    x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
    read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
    if (x == 0) { putchar ('0'); return; }
    if (x < 0) { putchar ('-'); x = -x; }
    int poi = 0;
    while (x) {
        For_Print[++poi] = x % 10 + '0';
        x /= 10;
    }
    while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
    write (x); putchar (ch);
}

const LL Mod = 1e9 + 7;

LL square (LL x) { return (x * x) % Mod; }
void DEL (LL &x, LL y) { ((x -= y) < 0) && (x += Mod); }
void ADD (LL &x, LL y) { ((x += y) >= Mod) && (x -= Mod); }

const int Maxn = 1e6;
const int Maxk = 20;

int q, op, n;

int cnt, primes[Maxn + 5];
int num[Maxn + 5], px[Maxn + 5];
bool vis[Maxn + 5];
void Euler () {
    rep (i, 2, Maxn) {
        if (vis[i] == 0) {
            num[i] = 1;
            px[i] = i;
            primes[++cnt] = i;
        }
        rep (j, 1, cnt) {
            if (primes[j] > Maxn / i) break;
            vis[primes[j] * i] = 1;
            px[i * primes[j]] = primes[j];
            if (i % primes[j] == 0) {
                num[i * primes[j]] = num[i];
                break;
            }
            num[i * primes[j]] = num[i] + 1;
        }
    }
}
LL f[Maxn + 5][Maxk + 5];
void Init () {
    f[0][0] = 1; rep (j, 1, Maxk) f[0][j] = 2;
    rep (i, 1, Maxn) {
        f[i][0] = 1;
        rep (j, 1, Maxk)
            f[i][j] = (f[i][j - 1] + f[i - 1][j]) % Mod;
    }
}

int main () {
	//freopen ("D:\\lihan\\1.in", "r", stdin);
	//freopen ("D:\\lihan\\1.out", "w", stdout);

    Euler ();
    Init ();

    read (q);
    while (q--) {
        read (op, n);
        if (op == 0) {
            print (1ll << num[n], '\n');
        }
        else {
            LL res = 1;
            while (n != 1) {
                int p = px[n], q = 0;
                while (n % p == 0) {
                    q++;
                    n /= p;
                }
                res = res * f[op][q] % Mod;
            }
            print (res, '\n');
        }
    }
    return 0;
}
<think>我们根据用户的问题,需要回答狄利克雷卷积的数学定义、编程实现以及算法应用。同时,参考提供的引用内容,我们可以整合相关信息。注意:用户要求使用中文回答,并且遵守LaTeX格式要求(行内公式用$...$,独立公式用$$...$$并单独成段)。步骤:1.数学定义:根据引用[2]和引用[5],给出狄利克雷卷积的定义和基本性质。2.编程实现:根据引用[3]和引用[1],给出狄利克雷卷积的代码实现,并解释代码。3.算法应用:根据引用[4]和引用[5],介绍狄利克雷卷积在数论问题中的应用,例如求和问题。注意:在回答中,我们尽量引用提供的参考资料,并在引用的末尾添加引用标识。下面开始组织回答:</think> ### 狄利克雷卷积详解 #### 1. 数学定义 狄利克雷卷积是数论中两个函数间的二元运算,定义如下: 对于两个数论函数 $f(n)$ 和 $g(n)$,其狄利克雷卷积为: $$(f * g)(n) = \sum_{d \mid n} f(d) \cdot g\left(\frac{n}{d}\right)$$ 其中 $\sum_{d \mid n}$ 表示对所有能整除 $n$ 的正因数 $d$ 求和[^2]。 **核心性质**: - **积性保持**:若 $f$ 和 $g$ 均为积性函数(即 $f(ab)=f(a)f(b)$ 当 $gcd(a,b)=1$),则卷积结果 $f*g$ 也是积性函数[^1]。 - **常用恒等式**: - $ε = μ * I$ (莫比乌斯反演) - $φ = μ * id$ (欧拉函数) - $σ = I * id$ (约数和函数) - $τ = I * I$ (约数个数函数)[^5] 其中 $ε$ 是单位函数($ε(1)=1$,其它为$0$),$I(n)=1$,$id(n)=n$。 #### 2. 编程实现 基于枚举因数的实现(时间复杂度 $O(n \log n)$[^1]): ```python def dirichlet_conv(f, g, n): """计算 f*g 的前 n 项""" h = [0] * (n+1) # 存储卷积结果 for i in range(1, n+1): # 枚举第一个函数参数 for j in range(1, n//i+1): # 枚举乘积位置 i*j <= n k = i * j h[k] += f[i] * g[j] # 累加到 h[i*j] return h[1:] # 返回下标1-n的结果 ``` **示例计算**: 设 $f = μ$(莫比乌斯函数),$g = I$(常数函数) ```python # 定义前10项 mu = [0,1,-1,-1,0,-1,1,-1,0,0] # μ(1)~μ(9) I = [0,1, 1, 1,1, 1,1, 1,1,1] # I(n)=1 print(dirichlet_conv(mu, I, 9)) # 输出 [1,0,0,0,0,0,0,0,0] 符合 μ*I=ε ``` #### 3. 算法应用 1. **莫比乌斯反演**: $$g(n) = \sum_{d|n} f(d) \iff f(n) = \sum_{d|n} μ(d) g(\frac{n}{d})$$ 常用于简化含 $\gcd$ 的求和式,如: $$\sum_{i=1}^n \sum_{j=1}^m \gcd(i,j) = \sum_{d=1}^{\min(n,m)} \phi(d) \lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor$$[^4] 2. **筛法优化**: 在欧拉筛过程中同步计算积性函数的卷积,可将预处理复杂度降至 $O(n)$。 3. **数论函数转换**: 利用 $φ = μ * id$ 快速计算欧拉函数: ```python def euler_from_mu(n): phi = [0]*(n+1) for d in range(1, n+1): for j in range(d, n+1, d): # j = k*d phi[j] += mu[d] * (j//d) # μ(d)*id(j/d) return phi ``` **典型应用场景**: - 求解 $\sum_{i=1}^n \sum_{j=1}^m \gcd(i,j)^k$ 类问题 - 杜教筛等亚线性时间求和算法 - 积性函数前缀和的线性预处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值