莫比乌斯反演
前置知识
数论分块
数论分块,还可以称为整除分块。
可以注意到,对于一个数 \(n\) , \(\lfloor \frac{n}{i}\rfloor\) 呈现一种块状分布,并且对于 \(\lfloor \frac{n}{i}\rfloor\) 的一块来说,最后一个数是\(\lfloor\frac{n}{\lfloor \frac{n}{i}\rfloor}\rfloor\) 。
证明:
而这时我们就可以一块一块的处理了,而块数数量级为 \(O(\sqrt{n})\) ,证明如下:
- 对于 \(i\leq\sqrt{n}\) ,就算每个 \(i\) 取值都不同, \(\lfloor \frac{n}{i}\rfloor\) 最多也只有 \(\sqrt{n}\) 种取值。
- 对于 \(i\geq\sqrt{n}\) ,\(\lfloor \frac{n}{i}\rfloor\leq\sqrt{n}\) ,自然最多有 \(\sqrt{n}\) 种取值。
- 所以 \(\lfloor \frac{n}{i}\rfloor\) 的取值有 \(O(\sqrt{n})\) 种。
我们举个例子,求:
乍一看和分块没有联系,那么我们化一下:
其中 \(k\) 的区间和是可以 \(O(1)\) 求的,而通过整除分块,我们也可以 \(O(\sqrt{n})\) 求出后面的减项的区间和,这样就吧 \(O(n)\) 的复杂度降到了 \(O(\sqrt{n})\)
积性函数
如果一个函数 \(f\) 有:
我们就把 \(f\) 称作积性函数。
常见的积性函数有:
- \(\varphi\) :欧拉函数
- \(I\) :不变函数,恒为 \(1\) 。
- \(x^a\) :幂函数
- \(\epsilon\) :单位元,只有 \(\epsilon(1)=1\) ,其它为 \(0\) 。
- \(d\) :约数个数函数
- \(\sigma\) :约数和函数
- \(\mu\) :莫比乌斯函数,详见下文
这里有一个小结论:
如果 \(f\) 是积性函数,那么 \(g\) 也是积性函数。
证明:
由于积性函数性质特殊,我们经常需要线性筛来求。
Dirichlet 卷积
定义 \(Dirichlet\) 卷积为:
卷积满足的定律有:
-
交换律:
这里有个小定理:
很显然成立,因为 \(n\) 的因数是成对的。
那么就显然:
-
结合律:
同样有一个小技巧,对于二重和式:
\[\sum_{d|n}\sum_{x|d}F(d,x) \]我们可以将 \(d=lx\) 代入式中,从而将 \(x\) 换成第一个枚举项:
\[\sum_{d|n}\sum_{x|d}F(d,x)=\sum_{x|n}\sum_{xl|n}F(lx,x) =\sum_{x|n}\sum_{l|\frac{n}{x}}F(xl,x) \]应用到结合律证明中:
\[(f*g)*h=f*(g*h)\\ \implies \sum_{d|n}\sum_{x|d}f(x)g(\frac{d}{x})h(\frac{n}{d})=\sum_{d|n}\sum_{x|d}f(\frac{n}{d})g(\frac{d}{x})h(x)\\ \implies\sum_{d|n}\sum_{x|d}f(x)h(\frac{n}{d})=\sum_{d|n}\sum_{x|d}f(\frac{n}{d})h(x)\\ \implies \sum_{x|n}\sum_{l|\frac{n}{x}}f(x)h(\frac{n}{lx})=\sum_{d|n}\sum_{x|d}f(\frac{n}{d})h(x)\\ \implies \sum_{x|n}\sum_{l|x}f(\frac{n}{x})h(\frac{x}{l})=\sum_{d|n}\sum_{x|d}f(\frac{n}{d})h(x)\\ \implies \sum_{x|n}\sum_{l|x}f(\frac{n}{x})h(l)=\sum_{d|n}\sum_{x|d}f(\frac{n}{d})h(x) \]可以发现只是换了个符号,两式等价。
-
分配律:
\[\sum_{d|n}[f(d)+g(d)]h(\frac{n}{d})=\sum_{d|n}f(d)h(\frac{n}{d})+\sum_{d|n}g(d)h(\frac{n}{d}) \]显然成立。
常见卷积:
-
\(d=I*I=\sum_{d|n} 1\)
-
\(n=\varphi*I=\sum_{d|n}\varphi(d)\) ,这个卷积被称作 欧拉反演 ,证明如下:
对于质数的幂次 \(p^k\) :
\[\sum_{d|p^k}\varphi(d)=1+(p-1)+(p^2-p)+\cdots+(p^k-p^{k-1})=p^k \]又因为此式是积性函数,所以对于任意的 \(x=p_1^{a_1}p_2^{a_2}\cdots p_r^{a_r}\) :
\[\sum_{d|x}\varphi(d)=\sum_{d|p_1^{a_1}}\varphi(d)\sum_{d|p_2^{a_2}}\varphi(d)\cdots\sum_{d|p_r^{a_r}}\varphi(d)=p_1^{a_1}p_2^{a_2}\cdots p_r^{a_r}=x \] -
\(\sigma=n*I=\sum_{d|n}d\) ,代入上面那个结论可得:\(\sigma=\varphi*d\)
莫比乌斯函数
莫比乌斯函数 \(\mu\) 如下定义:
\([n==1]\) 表示一个 \(bool\) 表达式,为真时返回 \(1\) ,否则返回 \(0\) 。
卷积表示为 :
而详细的 \(\mu\) 值为:
第二项表示,对 \(x\) 因式分解后,如果质因子都是一次幂,\(\mu\) 值为 \(1\) , 如果有任意一个质因子幂次大于等于 \(2\) ,那么 \(\mu\) 值为 \(0\) 。
看到这里,大概已经很懵逼 \(\mu\) 如此奇怪的值了,实际上,\(\mu\) 的定义是为了进行反演,所以并不是基于单个值,而是基于卷积,单个值只是解出来的,所以讨论单个值没有意义。
莫比乌斯反演
对于函数:
莫比乌斯函数可以使得:
证明:(用到了上面证结合律和交换律的小技巧)
除了 \(d|n\) 型以外,莫比乌斯反演还更常用到 \(n|d\) 的形式:
证明:
当然也可以用卷积定义去证明:
有了莫比乌斯反演,我们在题目中就可以通过可能较好表示的 \(f\) 的卷积来反推 \(f\)。
例题
YY的GCD
题目是让求:
多组数据,\(T\leq1e4\),\(N,M\leq1e7\) 。
首先不妨设 \(N\leq M\)
然后我们设 \(f(x)\) 是 \(gcd\) 为 \(x\) 的数的对数 ,\(F(x)\) 是公约数(不是最大公约数)的个数。
显然有:
而我们要求的是:
如果直接枚举 \(p\) ,后面得一个个算 \(\lfloor \frac{N}{d}\rfloor\),需要 \(O(T\times prime\times\sqrt{N})\) 的复杂度 。是过不了这题的。
我们需要换一个枚举项,好预处理。
我们尝试枚举 \(d\) :
如此一来前半部分可以数论分块求出。问题就到了后半部分。
我们发现,后半部分竟然可以线性筛出!
我们设 \(h(x)=\sum_{p|x}\mu(\frac{x}{p})\) :
-
对于 \(x\) 是质数的情况,很显然 \(h(x)=\mu(1)=1\) 。
-
对于递推情况 \(h(x\times p),\;p\in prime\) :
若 \(p\) 不是 \(x\) 已有的质因子,原来的 \(\mu\) 项中都会多一个质因子,值取负,再加上新增的一项 \(\mu(x)\) 。
若 \(p\) 是 \(x\) 已有的质因子,但是只有一次幂,那么,除了 \(\mu(\frac{x\times p}{p})\) 的一项外,其他项都变为 \(0\) ,所以值为 \(\mu(x)\) 。
若 \(x\) 中有大于等于二次幂的 \(p\) 因子,值直接为 \(0\) 。
总结起来就是:
那么线性筛出 \(h\) ,并处理出其前缀和,然后数论分块就可以达到 \(O(N+T\times \sqrt{N})\) 的复杂度了。
\(\frak Code\)
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 10000000
long long ans;
int n,m,t;
int tot;
int prime[1000050],inp[10000050],mu[10000050],su[10000050],sum[10000050];
void prework(){
inp[0]=inp[1]=1;
mu[1]=1;
su[1]=0;
sum[0]=sum[1]=0;
for(int i=2;i<=MAXN;i++){
if(!inp[i]){
prime[++tot]=i;
mu[i]=-1;
su[i]=mu[1];
}
for(int j=1;j <= tot && 1ll*prime[j]*i <= MAXN;j++){
int tp=prime[j]*i;
inp[tp]=1;
if(i%prime[j]==0){
mu[tp]=0;
if((i/prime[j])%prime[j] == 0)
su[tp]=0;
else
su[tp]=mu[i];
break;
}
su[tp]=mu[i]-su[i];
mu[tp]=-1*mu[i];
}
sum[i]=sum[i-1]+su[i];
}
}
int main()
{
scanf("%d",&t);
prework();
while(t--){
ans=0;
scanf("%d %d",&n,&m);
if(m<n) swap(n,m);
for(int i=2;i<=n;i++){
int lt=min(n,min(n/(n/i),m/(m/i)));
ans+=1ll*(sum[lt]-sum[i-1])*(n/i)*(m/i);
i=lt;
}
printf("%lld\n",ans);
}
return 0;
}
声明及感谢
部分内容参考自: