莫比乌斯反演与狄利克雷卷积
Part0:符号约定
\([p]\):艾弗森记号.对于命题\(p\),当\(p\)成立时,\([p]\)为\(1\),否则为\(0\).
\(a|b\):\(b\)被\(a\)整除.即\(\exist k\in\Z\)使得\(b=ka\).
\(a\perp b\):\(a\)与\(b\)互质,即\(\gcd(a,b)=1\).特殊地,\(1\perp 1\)也成立.
本文中的函数,未经特殊说明,均指数论函数,即定义在正整数集\(\N^+\)上的一类函数.本文中\(n,m,t,i,j,k\)默认表示某个正整数,\(p\)默认为某个素数.
Part1:整除分块
我们的目标是求数论函数
的值.显然,我们可以在\(O(n)\)时间内朴素地求出该函数的值.但是,画出\(\left\lfloor\frac ni\right\rfloor\)的图像可知,该函数有许多值是一样的,并且呈块状分布.并且更一般地,每一个块的最后一个数就是\(\left\lfloor n/(\left\lfloor \frac ni\right\rfloor)\right\rfloor\)
于是,我们就有一种\(O(\sqrt n)\)的算法:
\(\text{CALC_DIVIDE_SUM}(n):\)
\(res\leftarrow 0\)
\(l\leftarrow 1\)
\(r\leftarrow 0\)
\(\mathbf{while}\ l\le n:\)
\(\quad r\leftarrow \left\lfloor n/(\left\lfloor \frac nl\right\rfloor)\right\rfloor\)
\(\quad res\leftarrow res+(r-l+1)\cdot \left\lfloor\frac nl\right\rfloor\left\lfloor\frac ml\right\rfloor\)
\(\quad l\leftarrow r+1\)
\(\mathbf{return}\ res\)
C++实现如下:
inline int divide_sum(int n)
{
for(int l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans+=(r-l+1)*(n/l);
}
return ans;
}
当然,我们有时候是求函数的和,这时候就要将r-l+1
替换为函数在这一段区间上的和.
如果我们要求
也可设计出以下\(O(\sqrt n+\sqrt m)\)的算法:
\(\text{CALC_DIVIDE_SUM2}(n,m):\)
\(res\leftarrow 0\)
\(l\leftarrow 1\)
\(r\leftarrow 0\)
\(\mathbf{while}\ l\le \min(n,m):\)
\(\quad r\leftarrow \min(\left\lfloor n/(\left\lfloor \frac nl\right\rfloor)\right\rfloor,\left\lfloor m/(\left\lfloor \frac ml\right\rfloor)\right\rfloor)\)
\(\quad res\leftarrow res+(r-l+1)\cdot \left\lfloor\frac nl\right\rfloor\left\lfloor\frac ml\right\rfloor\)
\(\quad l\leftarrow r+1\)
\(\mathbf{return}\ res\)
C++实现如下:
inline int divide_sum(int n,int m)
{
for(int l=1,r;l<=std::min(n,m);l=r+1)
{
r=std::min(n/(n/l),m/(m/l));
ans+=(r-l+1)*(n/l)*(m/l);
}
return ans;
}
Part2:狄利克雷卷积
定义两个数论函数\(f,g\)的狄利克雷卷积(Dirichlet convolution)为:
狄氏卷积有以下性质:
(交换律)\(1.f*g=g*f\);
(结合律)\(2.f*g*h=(f*g)*h=f*(g*h)\),这是因为
(分配律)\(3.(f+g)*h=f*h+g*h\);
(数乘律)\(4.(mf)*g=f*(mg)=m(f*g)\);
(单位元)\(5.e*f=f\),其中
(逆元)\(6.\)对于每个\(f(1)\ne0\)的函数\(f\),都存在一个函数\(g\)使得\(f*g=e\),记作\(g=f^{-1}\).
从上述结论可以看出,所有\(f(1)\ne0\)的函数对狄氏卷积构成一个群.
那么,如何求出一个函数的逆呢?
事实上,有
因为
Part3:积性函数
若一个数论函数\(f\)满足:当\(n\perp m\)时,有\(f(nm)=f(n)f(m)\),则称之为积性函数(multiplicative function).
一些常见的积性函数有:\(e(n)=[n=1];\text{id}(n)=n;\text{id}^k(n)=n^k;1(n)=\text{id}^0(n)=1\).事实上,这些函数是完全积性函数(completelty multiplicative function),即不论\(n,m\)是否互质,都有\(f(nm)=f(n)f(m)\).
另外,\(n\)的因数个数\(\sigma_0(n)\)和欧拉函数\(\varphi(n)\)(\(1,2,\dots,n\)中与\(n\)互质的数的个数)也是积性函数.证明如下:
引理 如果\(n\perp m\),我们发现每个\(nm\)的约数\(t\)都可以分解成一个\(n\)的约数\(\gcd(n,t)\)和一个\(m\)的约数\(\gcd(m,t)\)的积,并且这种分解是一一对应的.
这是因为\(n\perp m,t|nm\Rightarrow t=\gcd(t,nm)=\gcd(t,n)\gcd(t,m)\),而且\(a|n,b|m\Rightarrow ab|nm\),并且,\(a|n,b|n\Rightarrow\gcd(ab,n)=a\).
于是\(\sigma_0(nm)=\sigma_0(n)\sigma_0(m)\).
同样的,\(t\perp nm\Leftrightarrow t\perp n,t\perp m\Leftrightarrow(t\bmod n)\perp n,(t\bmod m)\perp m\),所以每个\(1,2,\dots,nm\)之间的与\(nm\)互质的数\(t\)都可以对应到一个\(1,2,\dots,n\)的与\(n\)互质的数\(t\bmod n\)和一个\(1,2,\dots,m\)的与\(m\)互质的数\(t\bmod m\).
并且根据中国剩余定理,这种对应是一一对应的(即已知\(a\perp n,b\perp m\)后可以唯一确定一个\(1,2,\dots,nm\)之间的\(t\)使得\(t\bmod n=a, t\bmod m=b\),且\(t\perp nm\)).因此\(\varphi(nm)=\varphi(n)\varphi(m)\).
下面我们来证明两个结论:
\(1.\)两个积性函数的狄氏卷积是积性函数.
由于上面的引理,又因为\(n\perp m,a|n,b|m\Rightarrow a\perp b\),于是若\(n\perp m\),则
\(2.\)积性函数的逆是积性函数.
对\(nm\)的大小运用数学归纳法.设\(f^{-1}=g\),则
\(1.\)当\(nm=1\)时,\(g(1)=1\),结论成立;
\(2.\)假设\(nm>1\),当\(n'm'<nm\)时有\(g(n'm')=g(n')g(m')\),
Part4:积性函数的线性筛
根据算数基本定理,由于每个正整数\(n\)可以写成惟一的分解形式\(n=\prod\limits_{i=1}^{m}p_i^{\alpha_i}\),所以
所以,一个积性函数在素数幂处的取值决定了整个积性函数.并且,由于在线性筛素数时我们可以顺便求出每个数的最小质因数\(p_1\)以及最小质因数的次数\(\alpha_1\)以及\(n/p_1^{\alpha_1}\),就可以利用递推式\(f_n=f(p_1^{\alpha_1})f(n/p_1^{\alpha_1})\)直接计算\(f\)了.
另外,由于\(\sigma_0\)和\(\varphi\)在素数幂处的取值容易得到:\(\sigma_0(p^k)=k+1,\varphi(p^k)=p^{k-1}(p-1)\),所以
因\(\varphi*1\)也是积性函数,又\((\varphi*1)(p^k)=p^k\),故有\(\text{id}=\varphi*1\).
Part5:莫比乌斯函数与莫比乌斯反演
我们定义\(1\)的逆是\(\mu\),称为莫比乌斯函数(Mobius function).这样的话,若\(g=f*1\),则就有\(f=g*\mu\),更一般地,
或等价地,
上述公式称为莫比乌斯反演公式(Mobius inversion formula).或者类似地,我们可以发现\(\text{id}^k\)的逆是\(f(n)=\mu(n)n^k\),于是若
就有
比如,可得\(\varphi=\mu*\text{id}\),即
那么,如何求\(\mu\)呢?
由于\(1\)是积性的,而\(\mu\)是\(1\)的逆,所以\(\mu\)也是积性的.容易计算出:
于是,
莫比乌斯函数有以下性质:
\(1.\)对于任意的正整数\(n\),有
\(2.\)对于任意的正整数\(n\),有
运用上面的性质\(1\),我们容易得出\(\mu\)的线性筛代码:
\(\text{LINEAR_SIEVE_MU}(n):\)
\(\mu\leftarrow\ new\ array\ filled\ with\ 0\)
\(\mathbf{for}\ i\leftarrow 1\ \mathbf{to}\ n:\)
\(\quad tar\leftarrow [i=1]//tar\text{ is the sum of Mobius functions so far}\)
\(\quad \delta\leftarrow tar-\mu[i]//\delta\text{ is the differences between the functions and the sum}\)
\(\quad \mu[i]\leftarrow \delta\)
\(\quad j\leftarrow 2i\)
\(\quad \mathbf{while}\ j\le n:\)
\(\quad\quad mu[j]\leftarrow mu[j]+\delta\)
\(\quad\quad j\leftarrow j+i\)
\(\mathbf{return}\ \mu\)
C++实现如下:
void sieve_mu(int n,int mu[]) //mu是储存结果的数组
{
for(int i=1;i<=n;++i)
{
int tar=(i==1?1:0);
int delta=tar-mu[i];
mu[i]=delta;
for(int j=2*i;j<=n;j+=i)
mu[j]+=delta;
}
}
我们也可以在筛素数的同时筛出\(\mu\):
\(\text{LINEAR_SIEVE_PRIME_AND_MU}(n):\)
\(\mu,p\leftarrow\ new\ array\ filled\ with\ 0\)
\(vis\ new\ array\ filled\ with\ \mathbf{FALSE}//vis\text{ is used to label numbers whether has divisors or not}\)
\(\mu[1]\leftarrow 1\)
\(\mathbf{for}\ i\leftarrow 2\ \mathbf{to}\ n:\)
\(\quad \mathbf{if}\ vis[i]=\mathbf{FALSE}:\)
\(\quad\quad p.append(i)\)
\(\quad\quad mu[i]\leftarrow-1\)
\(\quad \mathbf{for}\ each\ prime\ p_0\ \mathbf{in}\ p:\)
\(\quad\quad vis[p_0\cdot i]\leftarrow \mathbf{TRUE}\)
\(\quad\quad \mathbf{if}\ p|i:\)
\(\quad\quad\quad \mathbf{break}\)
\(\quad\quad \mathbf{else}:\)
\(\quad\quad\quad \mu[i\cdot p_0]\leftarrow-\mu[i]\)
\(\mathbf{return}\ \mu,p\)
C++实现如下:
void sieve_mu(int n,int mu[],int p[],int vis[],int& cnt)
{
memset(vis,0,sizeof(vis));
mu[1]=1;
cnt=0;
for(int i=2;i<=n;++i)
{
if(!vis[i])
{
p[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt&&p[j]*i<=n;++j)
{
vis[p[j]*i]=1;
if(i%p[j]==0)
break;
else
mu[i*p[j]]=-mu[i];
}
}
}
Part6:简单例题
LG P2257 YYY的GCD
对于\(n,m\),我们有\(d|\gcd(a,b)\Leftrightarrow d|a,d|b\).于是,对一个关于\(\gcd(a,b)\)的函数\(f(\gcd(a,b))\),如果有\(f(n)=\sum\limits_{d|n}g(d)\)的话,就可以写成\(\sum\limits_{d|a,d|b}g(d)\)了.
考虑令\(f(n)=[n是素数]\).我们要找到一个\(g\)使得\(f=1*g\),那么\(g=\mu*f\),
于是我们可以这样求出\(g\):
\(\text{SIEVE_G}(n):\)
\(g\leftarrow\ new\ array\ filled\ with\ 0\)
\(\mu,p\leftarrow \text{LINEAR_SIEVE_PRIME_AND_MU}(n)\)
\(\mathbf{for}\ each\ p_0\ \mathbf{in}\ p:\)
\(\quad i\leftarrow 1\)
\(\quad \mathbf{while}\ i\cdot p_0\le n:\)
\(\quad\quad g[i\cdot p]\leftarrow g[i\cdot p]+\mu[i]\)
\(\mathbf{return}\ g\)
复杂度为\(O(\sum\limits_{p\le n}(n/p))=O(n\log\log n)\).
我们要求的是
先预处理\(g\)的前缀和,然后就可以用整除分块之后\(O(\sqrt n+\sqrt m)\)回答每个询问.C++实现如下:
const int Maxn=1e7+5,MaxLogn=15;
int n,m,cnt,p[Maxn/MaxLogn],mu[Maxn],g[Maxn];
bool vis[Maxn];
void sieve()
{
mu[1]=vis[1]=1;
for(int i=2;i<Maxn;++i)
{
if(!vis[i])
{
p[++cnt]=i;
mu[i]=-1;
g[i]=1;
}
for(int j=1;j<=cnt&&i*p[j]<Maxn;++j)
{
int x=i*p[j];
vis[x]=1;
if(i%p[j]==0)
{
g[x]=mu[i];
mu[x]=0;
break;
}
else
{
g[x]=-g[i]+mu[i];
mu[x]=-mu[i];
}
}
g[i]+=g[i-1];
}
}
int main()
{
sieve();
int T;
scanf("%d",&T);
for(;T--;)
{
scanf("%d%d",&n,&m);
long long ans=0;
if(n>m)
swap(n,m);
for(int l=1,r;l<=n;l=r+1)
{
r=min(n/(n/l),m/(m/l));
ans+=1LL*(g[r]-g[l-1])*(n/l)*(m/l);
}
printf("%lld\n",ans);
}
}