• 算法学习笔记(24): 狄利克雷卷积和莫比乌斯反演


    狄利克雷卷积和莫比乌斯反演

    看了《组合数学》,再听了学长讲的……感觉三官被颠覆……


    狄利克雷卷积

    如此定义:

    (fg)(n)=xy=nf(x)g(y)

    或者可以写为

    (fg)(n)=d|nf(d)g(nd)


    特殊的函数

    • 单位根 ε:满足 fε=εf=f

    ε(n)={1,if n = 10,otherwise

    • 幂函数 Idk(n)=nk。特殊的,Id1(n)=n 为恒等函数,Id0(n)=1 为常函数,简记为 I
    • 除数函数 σk(n)=d|ndk。特殊的,σ1(n) 为因数和函数,简记为 σ(n)σ0(n) 为因数个数函数,简记为 τ(n)
    • 欧拉函数 φ(n)。质因数分解 n=pc11pc22...pckk,则 φ(n)=nki=1pi1pi

    这些函数都是积性函数,满足 gcd(i,j)=1f(ij)=f(i)f(j)


    函数之间的关系

    可能有不完整的地方……

    其中可以通过 I 转化的,都可以通过 μ 转换回去。考虑 Iμ=ε 的事实,后面会讲。

    除数函数和幂函数

    根据定义,我们有

    (IdkI)(n)=d|nIdk(d)=d|ndk=σk(n)

    IdkI=σk

    欧拉函数和恒等函数

    根据卷积:

    (φI)(n)=d|nφ(d)

    n=pk 时(p 为质数),有:

    d|nφ(d)=φ(1)+ki=1φ(pi)=1+ki=1pipi1=pm=d

    所以 (φI)(pk)=pk

    n 质因数分解为 pk,根据积性函数的定义,可知:

    (φI)(n)=n=Id1(n)

    莫比乌斯函数和欧拉函数

    应该把后面看完了再回来看这个……

    莫比乌斯函数定义为:

    μI=ε

    根据: φI=Id1,两边同时卷上 μ

    有:

    φIμ=Id1μφ=Id1μ


    狄利克雷卷积的逆元

    对于一个函数 f,我们可以如下的定义一个函数 g

    首先设 g(1)=1f(1)

    然后令 g(x)=1f(1)d|x,d>1g(d)f(xd)

    于是 (fg)=ε

    展开带入证明即可。


    莫比乌斯函数与莫比乌斯反演

    终于到这里了 QwQ

    我们定义莫比乌斯函数是 I 的逆函数,也就是说 (μI)=ε

    所以,在狄利克雷卷积中:

    μ(n)={1if n=10if xk,n=kx2(1)mn=p1p2...pm

    至于为什么强调狄利克雷卷积……后文会提及。

    莫比乌斯函数常用于以下形式

    g(n)=d|nf(d)f(n)=d|nμ(d)g(nd)

    或者可以写作:

    fI=gf=gμ

    而这就是莫比乌斯反演的核心公式。

    很简单的公式,根本无需记忆……

    求法

    和欧拉函数 φ 类似,可以通过线性筛的方法在 O(n) 内求出。

    vector<int> prms;
    int mob[N], notp[N];
    void getMob(int n) {
        mob[1] = 1;
        for (int i = 2; i <= n; ++i) {
            if (!notp[i])
                mob[i] = -1, prms.push_back(i);
    
            for (int p : prms) {
                int ip = i * p;
                if (ip > n) break;
                notp[ip] = 1;
                if (i % p == 0) {
                    mob[ip] = 0;
                    break;
                } else mob[ip] = -mob[i];
            }
        }
    }
    

    数论分块(整除分块)

    这部分虽然不属于莫比乌斯反演,但是在求很多相关问题的时候需要用到。

    开篇膜拜 Pecco 大佬:# 算法学习笔记(36): 莫比乌斯反演

    核心问题:求解 ni=1ni,n1012

    不难得知, ni 不同的取值只有 O(n) 个,并且是连续的。所以考虑对于每一种取值,求出有其总贡献。问题转化为,对于 i,需要求出满足 ni=nj 的最大的 j

    于是设 ni=k

    nj=kknjk+11k+1<jn1kjnkjnni

    也就是说,对于每一个取值 ni,最大在 nni 时满足。

    于是可以这样写出代码:

    for (int l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        ans += (r - l + 1) * (n / l);
    }
    

    练习题:[CQOI2007]余数求和 - 洛谷


    补充:现在我们考虑更极端的情况,例如求:

    f(n)=ni=1g(ni)

    其中:

    g(n)=ni=1ni

    写出来 n=1e9 的时候可以轻松过掉。其复杂度为 O(n34),证明见讨论。

    膜拜 @jijidawang in 讨论

    所以见到类似的数论分块套数论分块,大胆的写下去吧!


    其实数论分块不仅仅可以解决 ni 的问题,也可以套用在 i 的问题上。写法十分类似。这里就不做过多讲解。


    莫比乌斯反演的经典结构

    莫比乌斯反演的经典套路其实就是 ε,φ,μ,I,Id 的灵活应用和转换,以及交换合式的小技巧。

    其中有 ε(x)=[x=1]=d|xμ(d),Id(x)=x=d|xφ(x)

    结构1

    [gcd(i,j)=1]=ε(gcd(i,j))=d|gcd(i,j)μ(d)

    于是对于:

    ni=1mj=1[gcd(i,j)=1]=ni=1mj=1d|gcd(i,j)μ(d)

    在这里,有一个非常经典的处理方法:提取公因数

    也就是枚举 gcd(i,j)

    =min(n,m)d=1nii=1njj=1μ(d)=min(n,m)d=1ndmdμ(d)

    于是最终利用数论分块求即可。复杂度为 O(n+min(n,m))

    但是代码需要注意,每一次取小步跳:

    r = min(n / (n / l), m / (m / l));
    

    结构2

    原题:GCD SUM - 洛谷

    gcd(i,j)=Id1(gcd(i,j))=(Iφ)(gcd(i,j))=d|gcd(i,j)φ(d)

    于是求 ni=1mj=1gcd(i,j) 的方法与结构1类似即可。

    结构3

    τ(x)=k|x1τ(xy)=i|xj|y[gcd(i,j)=1]

    于是求 nx=1my=1τ(xy) 也就很简单了。

    结构4

    原题:YY的GCD - 洛谷

    P 表示质数集合,求:

    ni=1mj=1[gcd(i,j)P]

    我们首先提取公因数:

    =pPnpi=1mpj=1[gcd(i,j)=1]

    于是根据模型1:

    =pPmin(n,m)px=1μ(x)npxnpx

    接下来是一个非常经典的套路:枚举 T=px

    =min(n,m)T=1nTmTp|T,pPμ(Tp)

    于是问题转化为求 p|T,pPμ(Tp) 的前缀和,这样就可以单次询问 O(n)

    这完全可以通过埃氏筛筛出,复杂度 O(nloglogn),十分优秀。

    不过也可以通过线性筛筛出(因为这个函数非积性函数,所以这里不展开)。

    类似的题还有 [国家集训队]Crash的数字表格 / JZPTAB - 洛谷

    问题:ni=1mj=1lcm(i,j)

    考虑转化为 gcd,有

    lcm(i,j)=ijgcd(i,j)=gcd(i,j)×igcd(i,j)×jgcd(i,j)

    带入原式中,枚举 gcd(i,j),有:

    =min(n,m)d=1dndi=1mdj=1ij[gcd(i,j)=1]=min(n,m)d=1dndi=1mdj=1ijt|gcd(i,j)μ(t)=min(n,m)d=1dmin(n,m)dt=1μ(t)t2ndti=1imdtj=1j

    后面部分可以通过 g(n)=n(n+1)2 简化。

    =min(n,m)d=1dmin(n,m)dt=1μ(t)t2g(ndt)g(mdt)

    于是只需要预处理 μ(t)t2 的前缀和即可。

    但是显然数论分块套数论分块不够优秀,考虑继续优化。

    所以枚举 T=dt

    =min(n,m)T=1g(nT)g(mT)Tt|Tμ(t)t

    也就是说只需要线性求出 t|Tμ(t)t 即可(其满足积性)。

    [SDOI2014]数表 - 洛谷

    问题:ni=1mj=1σ(gcd(i,j))

    不过这道题要难一些,因为涉及到了更多的操作。

    主要就是将询问离线,按照限制的大小一一处理即可。

    毒瘤之神的考验 - 洛谷

    ni=1mj=1φ(ij)

    需要知道转化式子:

    φ(ij)=φ(i)φ(j)gcd(i,j)φ(gcd(i,j))

    φ 展开即可证明。

    于是可以得到玄妙的式子。

    然后通过值域分治求解即可。

    ……说着简单

    化简后有三个函数:

    f(n)=d|ndμ(nd)φ(d)g(n,t)=ni=1φ(it)h(n,m,t)=f(t)g(n,t)g(m,t)

    原式为:

    min(n,m)t=1h(nt,mt,t)

    显然,由于 h 的状态数很恨很多,所以考虑值域分治。

    nt 比较大的时候暴力求解,到很小的时候再利用预处理的前缀和快速求解。

    类似于 n 分治吧。

    于是你可以写出类下的代码:

    lint solve(lint n, lint m) {
        if (n > m) swap(n, m);
    
        lint ans = 0;
        for (lint i = 1; i <= min(n, S); ++i)
            (ans += h(i, n / i, m / i)) %= mod;
    
        for (lint l = S + 1, r; l <= n; l = r + 1) {
            r = min(n / (n / l), m / (m / l));
            ans += t[n / l][m / l][r] - t[n / l][m / l][l - 1] + mod;
            ans %= mod;
        }
    
        return ans;
    }
    

    其中 S 表示分治的边界,t 表示对于 h 在第三个参数位置的前缀和。

    预处理也是很简单:

    inline lint h(int t, int n, int m) {
        return f[t] * g[t][n] % mod * g[t][m] % mod;
    }
    
    void init(lint n = 1e5) {
        for (lint i = 1; i <= n; ++i) {
            for (lint j = 1; i * j <= n; ++j)
                (f[i * j] += (mod + mob[j]) % mod * i % mod * iphi[i] % mod) %= mod;
        }
    
        for (lint t = 1; t <= n; ++t) {
            g[t].resize(n / t + 1);
            for (int i = 1; i * t <= n; ++i)
                g[t][i] = (g[t][i - 1] + phi[i * t]) % mod;    
        }
    
        for (int i = 1; i < B; ++i) for (int j = 1; j < B; ++j) {
            int len = n / max(i, j);
            t[i][j].resize(len + 2);
            t[i][j][0] = 0;
            for (int k = 1; k <= len; ++k)
                t[i][j][k] = (t[i][j][k - 1] + f[k] * g[k][i] % mod * g[k][j] % mod) % mod;
        }
    
        S = N / B; 
    }
    

    很烦写而已。


    结构总结

    在这类莫比乌斯反演中,经典的两个套路:

    • 提取公因数

    • 枚举 T=px

    其实在 Pecco 的文章中,对于提取公因数这个方法有更加深刻的阐释。其不仅能应用在只有 i,j 两个变量的模型中,还可以扩展到更多的变量上。

    再次膜拜大佬:# 算法学习笔记(36): 莫比乌斯反演


    其实一般讲莫比乌斯反演到这里就没有了,但是我看了《离散数学》中的莫比乌斯反演一章。我发现两者根本不在同一个位阶上……这就是颠覆我认知的原因。

    所以这里我还要把莫比乌斯反演扩展出来。


    莫比乌斯再认识

    我们考虑这么一个情况:

    有集合 X 和偏序关系 (P(X),),设:

    F:P(X)RG:P(X)R

    其中:G(S)=TSF(T)

    则可以求得:F(S)=TS(1)|S||T|G(T)

    G 求的 F 的过程称为反解,其中,(1)|S||T| 就是莫比乌斯函数在这种情况下的取值,也称为容斥系数。

    顺便回顾一下基本容斥原理:

    A1,A2,,An 是有限集 S 的子集(代表 n 中属性?)定义 F(K)K 为下标集合,{1,2,,n})为集合 SAi(iK) 的元素的个数。也就是对于 sS,计数 s 当且仅当:

    iK,sAijK,sAj

    于是设 G(K)=LKF(L)

    其计数 S 中属于 j 不在 K 中的所有 Aj 的元素,以及属于其他的一些集合的元素的个数。因而还有:

    G(K)=|iKAi|

    根据上文,有

    F(K)=LK(1)|K||L|G(L)

    此时 F(Xn)(Xn={1,2,,n}) 计数的是那些仅属于满足 iXn 的集合 Ai 的元素,因此:

    F(Xn)=|iXn¯Ai|

    带入 (1) 中可以得到:

    |¯A1¯A2¯An|=LXn(1)n|L||iLAi|

    如过等价的利用 L 的补集,那么我们有:

    |¯A1¯A2¯An|=JXn(1)J|iJAi|

    这就是基本的容斥原理。


    二项式反演

    为什么突然到这里了……

    二项式反演可以说是上面内容的一种特殊形式。其满足:

    |S|=|T|F(S)=F(T),G(S)=G(T)

    此时我们可以直接通过集合大小表示:F(S)=f(|S|),G(S)=g(|S|)

    于是对于 G(K)=LKF(L),合并相同大小的子集,即可得到:

    g(k) = \sum_{l = 0}^{k} {k \choose l} f(l)

    根据反演,也就有:

    f(k) = \sum_{l = 0}^k (-1)^{k - l} {k \choose l} g(l)

    这也就是二项式反演在此的推导,这里的莫比乌斯函数 \mu,后文再说。


    扩展到偏序集

    在此,我们扩展到任意有限偏序集 (X, \le)。不过为了得到莫比乌斯函数,我们首先考虑二元变量。

    \mathbb{F}(X) 是满足只要 x \not \le y 就有 f(x, y) = 0 的所有实数函数的集合。

    f: X \times X \to \R

    我们如此定义卷积 h = f * g

    h(x, y) = \begin{cases} \sum_{\{z: x \le z \le y\}} f(x, z)g(z, y) & (x \le y) \\ 0 & otherwise \end{cases}

    不难证明卷积满足结合律,这部分留个读者思考。

    于是,我们重新定义如下函数:

    • 单位函数(克罗内克 delta 函数):

    \delta(x, y) = \begin{cases} 1 & \text{if } x = y \\ 0 & \text{otherwise } \end{cases}

    • 常数函数(zeta function):

    \zeta(x, y) = \begin{cases} 1 & \text{if } x \le y \\ 0 & \text{otherwise} \end{cases}

    至于莫比乌斯函数,与上文的定义类似,也就是 \zeta 的逆函数:

    \mu * \zeta = \delta

    于是通过卷积的定义,我们得到:

    \sum_{\{z: x \le z \le y\}} \mu(x, z)\zeta(z, y) = \delta(x, y) \qquad (x \le y)

    或等价的:

    \sum_{\{z: x \le z \le y\}} \mu(x, z) = \delta(x, y) \qquad (x \le y) \tag{2.1}

    而等式 (2.1) 意味着,对于所有的 x:

    \mu(x, x) = 1

    以及:

    \mu(x, y) = -\sum_{\{z: x \le z \lt y\}} \mu(x, z) \qquad (x < y)

    至于莫比乌斯反演,无非还是:

    f * \zeta = g \iff f = g * \mu


    于是我们重新思考二项式反演,其实就是偏序集 (P(X_n), \subseteq) 上的莫比乌斯反演。

    ABX_n 的子集,且 A \subseteq B,有 \mu(A, B) = (-1)^{|B| - |A|}

    这可以通过归纳假设证明,这里不过多展开。


    开篇讲的狄利克雷卷积上的莫比乌斯反演,其实就是偏序集 (\Z, |) 上的莫比乌斯反演。

    这东西谁都知道,一元的莫比乌斯函数 \mu(x) 怎么求。不过我们的目标是计算该偏序集的 \mu(a, b)

    但是,由于如果 a | b\mu(a, b) = \mu(1, \frac ba)。所以我们只需要算 \mu(1, x) 即可。

    \mu(x) 其实就是 \mu(1, x) ……


    考虑离散傅立叶变换。

    越扯越远了……QwQ

    不了解离散傅立叶变换的可以参考:算法学习笔记(17): 快速傅里叶变换(FFT)

    我们不是有:

    h(\omega^x) = \sum_{k = 0}^{n - 1} c_k \omega^{kx}

    我们整理一下重新写出:

    g(x) = \sum_{k = 0}^{n - 1} \omega^{kx} f(k)

    根据离散傅立叶逆变换,则有:

    f(x) = \frac 1n \sum_{k = 0}^{n - 1} \omega^{-kx} g(k)

    其中,\frac 1n \omega^{-kx} 就是容斥系数,\mu(k, x)


    最后的最后,提一个题吧:[春季测试 2023] 幂次 - 洛谷

    其实也可以通过容斥(求 \mu)求解,但并非反演。

    参见博客:幂次 - Jijidawang

  • 相关阅读:
    如何利用监控保障发布质量?
    kafka消息发送者
    谷歌浏览器翻译插件,扩展程序,页面右键翻译
    云原生可观测性平台-云监控
    深入学习Synchronized各种使用方法
    指针之野指针系列(2):如何规避野指针
    微信打开https网址会被拦截,无域名
    Matlab 中值滤波原理分析
    机器学习笔记 - 使用 PyTorch 的多任务学习和 HydraNet
    2022卡塔尔世界杯来临,体育界最新创意二维码案例大盘点!
  • 原文地址:https://www.cnblogs.com/jeefy/p/17585963.html