算法学习笔记(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=p1c1p2c2...pkck,则 φ(n)=ni=1kpi1pi

这些函数都是积性函数,满足 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)+i=1kφ(pi)=1+i=1kpipi1=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μ

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

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

后来知道上式是莫比乌斯反演的因数形式,我们实际上还有倍数形式

g(n)=n|df(d)f(n)=n|dg(n)μ(dn)

可能在某些神秘的情况下有用。

求法

和欧拉函数 φ 类似,可以通过线性筛的方法在 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): 莫比乌斯反演

核心问题:求解 i=1nni,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)=i=1ng(ni)

其中:

g(n)=i=1nni

写出来 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)

于是对于:

i=1nj=1m[gcd(i,j)=1]=i=1nj=1md|gcd(i,j)μ(d)

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

也就是枚举 gcd(i,j)

=d=1min(n,m)i=1nij=1njμ(d)=d=1min(n,m)ndmdμ(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)

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

有另外一道题可以用到这个方法:1900D - Small GCD,具体见题解 # CF1900D - Small GCD 题解

结构3

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

于是求 x=1ny=1mτ(xy) 也就很简单了。

结构4

原题:YY的GCD - 洛谷

P 表示质数集合,求:

i=1nj=1m[gcd(i,j)P]

我们首先提取公因数:

=pPi=1npj=1mp[gcd(i,j)=1]

于是根据模型1:

=pPx=1min(n,m)pμ(x)npxnpx

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

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

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

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

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

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

问题:i=1nj=1mlcm(i,j)

考虑转化为 gcd,有

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

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

=d=1min(n,m)di=1ndj=1mdij[gcd(i,j)=1]=d=1min(n,m)di=1ndj=1mdijt|gcd(i,j)μ(t)=d=1min(n,m)dt=1min(n,m)dμ(t)t2i=1ndtij=1mdtj

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

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

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

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

所以枚举 T=dt

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

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

[SDOI2014]数表 - 洛谷

问题:i=1nj=1mσ(gcd(i,j))

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

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

毒瘤之神的考验 - 洛谷

i=1nj=1mφ(ij)

需要知道转化式子:

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

φ 展开即可证明。

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

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

……说着简单

化简后有三个函数:

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

原式为:

t=1min(n,m)h(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|

根据上文,有

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

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

F(Xn)=|iXnAi¯|

带入 (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)=l=0k(kl)f(l)

根据反演,也就有:

f(k)=l=0k(1)kl(kl)g(l)

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


扩展到偏序集

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

F(X) 是满足只要 xy 就有 f(x,y)=0 的所有实数函数的集合。

f:X×XR

我们如此定义卷积 h=fg

h(x,y)={{z:xzy}f(x,z)g(z,y)(xy)0otherwise

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

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

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

δ(x,y)={1if x=y0otherwise 

  • 常数函数(zeta function):

ζ(x,y)={1if xy0otherwise

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

μζ=δ

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

{z:xzy}μ(x,z)ζ(z,y)=δ(x,y)(xy)

或等价的:

(2.1){z:xzy}μ(x,z)=δ(x,y)(xy)

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

μ(x,x)=1

以及:

μ(x,y)={z:xz<y}μ(x,z)(x<y)

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

fζ=gf=gμ


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

ABXn 的子集,且 AB,有 μ(A,B)=(1)|B||A|

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


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

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

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

μ(x) 其实就是 μ(1,x) ……


考虑离散傅立叶变换。

越扯越远了……QwQ

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

我们有:

h(ωx)=k=0n1ckωkx

我们整理一下重新写出:

g(x)=k=0n1ωkxf(k)

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

f(x)=1nk=0n1ωkxg(k)

其中,1nωkx 就是容斥系数,μ(k,x)


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

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

参见博客:幂次 - Jijidawang

posted @   jeefy  阅读(381)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示