\documentclass[12pt,UTF8,titlepage]{article}
\usepackage[colorlinks,linkcolor=blue]{hyperref}
\usepackage{fontspec}
\usepackage{xunicode}
\usepackage{xltxtra}
\usepackage{amstext}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{ulem}
\author{wcz\footnote{Contact me:\href {mailto:aiyoupass@outlook.com}{aiyoupass@outlook.com}}}
\title{莫比乌斯反演}
\usepackage{xeCJK}
\setmainfont[BoldFont=LinLibertine_RZah.ttf,ItalicFont=LinLibertine_RIah.ttf,BoldItalicFont=LinLibertine_RZIah.ttf]{LinLibertine_Rah.ttf}
\setsansfont[BoldFont=SourceCodePro-BoldIt.ttf,ItalicFont=SourceCodePro-Regular.ttf,BoldItalicFont=SourceCodePro-Bold.ttf]{SourceCodePro-It.ttf}
\setmonofont[BoldFont=UbuntuMono-RI.ttf,ItalicFont=UbuntuMono-BI.ttf,BoldItalicFont=UbuntuMono-B.ttf]{UbuntuMono-R.ttf}
\setCJKmainfont{KaiTi_GB2312}
\lstset{numbers=left,
numberstyle= \tiny,
keywordstyle= \color{ blue!70},commentstyle=\color{red!50!green!50!blue!50},
frame=shadowbox,
,rulesepcolor= \color{ red!20!green!20!blue!20},escapeinside=``
%xleftmargin=2em,xrightmargin=2em, aboveskip=1em}
}
\begin{document}
\maketitle
\tableofcontents
\newpage
\section{前言}
本文内容大部分来自Oier PoPoQQQ 的课件。\ Download: \href {https://1drv.ms/p/s!AmXDWOyfksuQsT9QiF1Q8BANX4fn}{onedrive},
\href {https://pan.baidu.com/s/1hs1WWLY}{baidu pan},密码:6ug5\ \par 本文基本上由我学习相当于是制作的一篇学习笔记,但是将课件中的一些不完善的地方加以完善
使得更容易理解,加上了部分例题的代码
\section{引子}
介绍莫比乌斯反演之前我们先来看一个函数
\begin{equation}
F(n)=\sum_{d|n} f(d)
\end{equation}
\par 根据$F(n)$的定义
\begin{align*}
&F(1)=f(1)\\
&F(2)=f(1)+f(2)\\
&F(3)=f(1)+f(3)\\
&F(4)=f(1)+f(2)+f(4)\ &F(5)=f(1)+f(5)\ &F(6)=f(1)+f(2)+f(3)+f(6)\ &F(7)=f(1)+f(7)\ &F(8)=f(1)+f(2)+f(4)+f(8)\ &\cdots
\end{align*}
\par 于是我们便可以通过$F(n)$推导出$f(n)$
\begin{align*}
&f(1)=F(1)\\
&f(2)=F(2)-F(1)\\
&f(3)=F(3)-F(1)\\
&f(4)=F(4)-F(2)\\
&f(5)=F(5)-F(1)\\
&f(6)=F(6)-F(3)-F(2)+F(1)\\
&f(7)=F(7)-F(1)\\
&f(8)=F(8)-F(4)\\
&\cdots
\end{align*}
\par 在推导的过程中我们是否发现了一些规律?
\newpage
\section{莫比乌斯反演}
莫比乌斯反演\footnote{baike:莫比乌斯反演是数论数学中很重要的内容,可以用于解决很多组合数学的问题。}
\subsection{莫比乌斯反演定义}
\begin{equation}
F(n)=\sum_{d|n} f(n)=\sum_{d|n}\mu(d)f(\frac{n}{d})
\end{equation}
\par 其中$\mu(d)$为莫比乌斯函数\label{1},定义如下
\begin{equation}
\mu(d)=
\begin{cases}
1, &d=0\\
(-1)^k, &d=p_1p_2\cdots p_k,\forall p_i!=p_j\\
0, &others
\end{cases}
\end{equation}
\par 莫比乌斯函数的定义式\footnote{$\mu(n)=\delta_{\varpi(n)\Omega(n)}\lambda(n)$}
\subsection{莫比乌斯函数的性质}
\subsubsection*{(1)}
当n不等于1时,n所有因子的莫比乌斯函数值的和为0,\ \par 设$$F(n)=\sum_{d|n} \mu(d)$$
\par 那么
\begin{align}
F(n)=
\begin{cases}
1, &n=1\ 0, &n>0
\end{cases}
\end{align}
证明:
\begin{align*}
\textcircled{1}&
\text{当$n=1$的时候显然成立} \ \textcircled{2}&\text{当$n\not = 1$时},n=p_1^{a_1}p_2^{a_2} \cdots p_k^{a_k}\ &\because \mu(d)\not =0\Leftrightarrow d=p_1p_2\cdots p_t\ &\text{质因子个数为$r$的因子只有$C_k^r$个}\ &\therefore F(x)=C_k^0-C_k^1+C_k^2+\cdots +(-1)^kC_k^k=\sum_{i=0}^{k}(-1)^iC_k^i\ &\text{接下来只需证明}\sum_{i=0}^n(-1)^iC_n^i=0(n\in N_+)\text{即可}\ &\text{因为二项式定理}\ &(x+y)^n=\sum_{i=0}^nC_n^ix^iy^{n-i}\ &\text{令}x=-1,y=1,\text{代入即可得证。}
\end{align*}
\subsubsection*{(2)}对于$n\in N_+$有:
\begin{equation}
\sum_{d|n}\frac{\mu(d)}{d}=\frac{\phi(n)}{n}
\end{equation}
\begin{align*}
&\text{只需要令}F(n)=n,f(n)=\phi(n),\ &\text{代入}F(n)=\sum_{d|n} f(n)=\sum_{d|n}\mu(d)f(\frac{n}{d})\text{即可}\ &\text{那么就有}n=\sum_{d|n}\phi(d)\text{为什么成立?}
\end{align*}
\subsubsection*{(3)}积性函数
数论上积性函数的定义\ \begin{align*}
&\text{设函数$f(n),其中n\in N+$}\ &\text{对于任意$(x,y)=1$都有$f(xy)=f(x)f(y)$},\ &\text{则称$f(n)$为一个积性函数;}\ &\text{若对于任意$x,y$都有$f(xy)=f(x)f(y)$},\ &\text{则称$f(n)$为一个完全积性函数。}\ \end{align*}
\par 积性函数的性质\ \par \textcircled{1}$f(1)=1$\ \par \textcircled{2}积性函数的前缀和也是积性函数\ \\
\par 因为积性函数是积性函数,\par 因此可以通过线性筛求出莫比乌斯函数的值
\begin{lstlisting}[language={[ANSI]C}]
mu[1]=1;
for(i=2;i<=n;i++){
if(!not_prime[i]){
prime[++tot]=i;
mu[i]=-1;
}
for(j=1;prime[j]*i<=n;j++){
not_prime[prime[j]*i]=1;
if(i%prime[j]==0){
mu[prime[j]*i]=0;
break;
}
mu[prime[j]*i]=-mu[i];
}
}
\end{lstlisting}
\subsubsection {例题1:}
\href {http://www.lydsy.com/JudgeOnline/problem.php?id=2440}{BZOJ 2440 完全平方数}\\par {题目大意}:求第k个无平方因子数\footnote{无平方因子数(Square-Free Number),即分解之后所有质因数的次数都为1的数}\\par 做法:
首先二分答案,问题转化为求$\left[1,x\right]$之间有多少个无平方因子数\ 根据容斥原理可知,对于$sqrt(x)$之内所有的质数,
答案G(x)=0个质数平方倍数的个数-1个质数平方倍数的个数+2个质数平方倍数的个数-...,
那么对于偶数个质数平方对于答案的贡献就是正的,否则是负的,\\如果不是若干个互异质数的乘积,那么对答案没有影响,
\par 如何表示这个式子呢?\ \par 观察莫比乌斯函数的定义\ref{1},可以知道对于能对答案产生贡献的数$x$,$\mu(x)=(-1)^k$,其中$k$为$x$分解得到质数的个数
根据上述说明,那么可以得知结果\ \begin{equation}
G(x)=\sum_{i=1}^{\lfloor \sqrt{x}\rfloor}\mu(i)\lfloor \frac{x}{i^2}\rfloor
\end{equation}
\begin{lstlisting}[language={[ANSI]C}]
#include<iostream>
#include<cstdio>
#include<cmath>
#define N 100005
using namespace std;
bool not_prime[N];
int prime[N];
int mu[N];
int tot;
void Mu(int n){
int i,j;
mu[1]=1;
for(i=2;i<=n;i++){
if(!not_prime[i]){
prime[++tot]=i;
mu[i]=-1;
}
for(j=1;prime[j]*i<=n;j++){
not_prime[prime[j]*i]=1;
if(i%prime[j]==0){
mu[prime[j]*i]=0;
break;
}
mu[prime[j]*i]=-mu[i];
}
}
}
int can(int x){
int sum=0;
int s=floor(sqrt(x));
for(int i=1;i<=s;++i)
if(mu[i])
sum+=mu[i]*floor(x/(i*i));
return sum;
}
int main(){
Mu(N);int T,sum;
scanf("%d",&T);
while(T--){
scanf("%d",&num);
long long l=1,r=num<<1,mid;
while(l<r){
mid=(l+r)>>1;
if(can(mid)<num)
l=mid+1;
else r=mid;
}
printf("%lld\n",r);
}
return 0;
}
\end{lstlisting}
\subsection{莫比乌斯反演定理的证明}
\begin{equation}
F(n)=\sum_{d|n}f(d)\Rightarrow f(n)=\sum_{d|n}\mu(d)F(\frac{n}{d})
\end{equation}
证明:
\begin{align*}
f(n)=&\sum_{d|n}\mu(d)F(\frac{n}{d})\ =&\sum_{d|n}\mu(d)\sum_{k|\frac{n}{d}}f(k)\ =&\sum_{k|n}f(k)\sum_{d|\frac{n}{k}}\mu(d)\ =&f(n)
\end{align*}
形式二:
\begin{equation}
F(n)=\sum_{n|d}f(d)\Rightarrow f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d)
\end{equation}
证明同理,一般要用到的都是这种形式
\subsection{莫比乌斯反演的应用}
\par 主要是用于处理一些组合数问题。
\par 对于一些函数$f(n)$,如果我们很难直接求出它的值,而容易求出倍数和或约数和$F(n)$,
那么我们可以直接利用莫比乌斯反演来求得$f(n)$的值。
\paragraph{例:}
f(n)表示某一范围内(x,y)=n的数对的数量,\ F(n)表示某一范围内n|(x,y)的数对的数量\ 那么直接求f(n)并不是很好求,而F(n)求起来相对无脑一些,\ 那么我们可以通过对F(n)进行莫比乌斯 反演来求得f(n)。\
\subsubsection{例题2:}
\href {http://www.lydsy.com/JudgeOnline/problem.php?id=2301}{BZOJ 2301 Problem b}
\par 题目大意:询问有多少对$(x,y)$满足$x\in \left[ a,b\right] ,y\in \left[ c,d \right] $且$(x,y)=k$。
\par 根据容斥原理,这个题目就可以转化成
\begin{align*}
s_1=&\sum_{i=1}^{b}\sum_{j=1}^{d}\left[ (i,j)=k \right]\ s_2=&\sum_{i=1}^{a-1}\sum_{j=1}^{d}\left[ (i,j)=k \right]\ s_3=&\sum_{i=1}^{b}\sum_{j=1}^{c-1}\left[ (i,j)=k \right]\ s_4=&\sum_{i=1}^{a-1}\sum_{j=1}^{c-1}\left[ (i,j)=k \right]
\end{align*}
\par 其中答案为$s_1-s_2-s_3+s_4$\ \par 那么我们需要快速求出
\begin{equation}
\sum_{i=1}^a\sum_{j=1}^b \left[ (i,j)=k \right]
\end{equation}
\par 这个式子可以进一步转化为
\begin{equation}
\sum_{i=1}^{\lfloor \frac{a}{k}\rfloor}\sum_{j=1}^{\lfloor \frac{b}{k}\rfloor }\left[ (i,j)=k \right]
\end{equation}
\par 考虑莫比乌斯反演,
令
\begin{align}
f(k)=&\sum_{i=1}^{a}\sum_{j=1}^{b} \left[ (i,j)=k \right]\ F(k)=&\sum_{i=1}^{a}\sum_{j=1}^{b} \left[ k|(i,j)\right]\ =&\lfloor \frac{a}{k}\rfloor \lfloor \frac{b}{k}\rfloor\ \text{反演后可得}f(k)=&\sum_{k|d}\mu(\frac{d}{k})F(d)\ =&\sum_{k|d}\mu(\frac{d}{k})\lfloor \frac{a}{d}\rfloor \lfloor \frac{b}{d}\rfloor
\end{align}
\par 分析可知这个算法的复杂度是$\Theta(n)$
\par 我们还需要对这个算法进行进一步优化
\par 因为$\lfloor \frac{a}{d}\rfloor $至多只有$2\sqrt{a}$个取值,
\par 那么$\lfloor \frac{a}{d}\rfloor \lfloor \frac{b}{d}\rfloor$至多只有$2(\sqrt{a}+\sqrt{b})$个取值
\par 因为使得$\lfloor \frac{a}{d}\rfloor \lfloor \frac{b}{d}\rfloor==i$成立的$i$值都是连续的,
\par 所以可以维护一个莫比乌斯函数的前缀和,
\par 这样就可以在$\Theta(\sqrt{n})$的时间内出解
\par {\color{red}枚举除法的取值在莫比乌斯反演的应用当中非常常用\label{2}}
\begin{lstlisting}[language={[ANSI]C}]
if(a>b)swap(a,b);
for(i=1;i<=a;i=last+1){
last=min(a/(a/i),b/(b/i));
re+=(a/i)*(a/i)*(sum[last]-sum[i-1]);
}
return re;
\end{lstlisting}
\newpage
代码异常好写
\begin{lstlisting}[language={[ANSI]C}]
#include<iostream>
#include<cstdio>
#include<cmath>
#define N 50005
#define inf 0x7fffffff
using namespace std;
bool not_prime[N];
int prime[N];
int sum[N];
int mu[N];
int tot;
void Mu(int n){
int i,j;
mu[1]=1;
for(i=2;i<=n;i++){
if(!not_prime[i]){
prime[++tot]=i;
mu[i]=-1;
}
for(j=1;prime[j]*i<=n;j++){
not_prime[prime[j]*i]=1;
if(i%prime[j]==0){
mu[prime[j]*i]=0;
break;
}
mu[prime[j]*i]=-mu[i];
}
}
for(int i=1;i<=n;++i)
sum[i]=sum[i-1]+mu[i];
}
int ans(int n,int m){
if(n>m)swap(n,m);
int last,i,re=0;
for(i=1;i<=n;i=last+1){
last=min(n/(n/i),m/(m/i));
re+=(n/i)*(m/i)*(sum[last]-sum[i-1]);
}
return re;
}
int main(){
Mu(N);
int T;
int a,b,c,d,k;
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
a--;c--;
a/=k;b/=k;c/=k;d/=k;
int Ans=ans(b,d)-ans(a,d)-ans(b,c)+ans(a,c);
printf("%d\n",Ans);
}
return 0;
}
\end{lstlisting}
BZOJ 10s但是luogu却莫名WA\ 全部加上long long 之后总算是过了\ 百思不得其解
\newpage
\subsubsection{例题3}
\href{http://www.lydsy.com/JudgeOnline/problem.php?id=2820}{BZOJ 2820 YY的GCD}\ 题目大意:求有多少数对$(x,y)$满足$x\in \left[ 1,n\right] ,y\in \left[ 1,m \right] $满足$(x,y)$为质数
做法:
\par 首先这个题目和上一个题目不一样的地方是他需要一个特殊的转化
\begin{align}
\text{令}
k=&min(n,m);\ ans=&\sum_{p}^k\sum_{i=1}^n\sum_{j=1}^m\left[ (i,j)=p\right] \ =&\sum_{p}^k\sum_{d=1}^k \mu(d)\lfloor \frac{n}{pd}\rfloor \lfloor
\frac{m}{pd}\rfloor \ \text{令}T=&pd\ ans=&\sum_{T=1}^{k}\lfloor \frac{n}{T}\rfloor \lfloor \frac{m}{T}\rfloor \sum_{p|T}^{k}\mu(\frac{T}{p})\ \text{令}F(k)=&\sum_{p|T}^k\mu(\frac{T}{p})\ \text{则}ans=&\sum_{T=1}^kF(k)\lfloor \frac{n}{T}\rfloor \lfloor \frac{m}{T}\rfloor \ \end{align}
线性筛素数的时候对$F(k)$前缀和处理\ 然后就转变为和例二\ref{2}一样的做法,枚举除法的取值了\\begin{lstlisting}[language={[ANSI]C}]
#include<iostream>
#include<cstdio>
#include<cmath>
#define N 10000000
#define ll long long
using namespace std;
bool not_prime[N];
ll prime[N];
ll sum[N];
ll mu[N];
ll tot;
void Mu(int n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!not_prime[i]){
prime[++tot]=i;
mu[i]=-1;
}
for(int j=1;prime[j]*i<=n;j++){
not_prime[prime[j]*i]=1;
if(i%prime[j]==0){
mu[prime[j]*i]=0;
break;
}
mu[prime[j]*i]=-mu[i];
}
}
for(int i=1;i<=tot;++i)
for(int j=1;j*prime[i]<=n;++j)
sum[j*prime[i]]+=(ll)mu[j];
for(int i=1;i<=n;++i)
sum[i]+=(ll)sum[i-1];
}
ll ans(int n,int m){
if(n>m)swap(n,m);
int last,i;ll re=0;
for(i=1;i<=n;i=last+1){
last=min(n/(n/i),m/(m/i));
re+=(ll)(n/i)*(m/i)*(sum[last]-sum[i-1]);
}
return re;
}
int main(){
Mu(N);
int T;
int a,b;
scanf("%d",&T);
while(T--){
scanf("%d%d",&a,&b);
ll Ans=ans(a,b);
printf("%lld\n",Ans);
}
return 0;
}
?\end{lstlisting}
这已经是第n次被long long卡一个小时以上了
\newpage
\subsubsection{例题4:}
\href{http://www.lydsy.com/JudgeOnline/problem.php?id=3529}{BZOJ 3529: [Sdoi2014]数表}\ \href{http://blog.csdn.net/PoPoQQQ/article/details/42076231}{选择膜拜Po爷}\ \par 题目大意:令F(i)为i的约数和,给定n,m,a,求\ $$\sum_{F(d)\leq a}F((i,j)=d)$$
因为a的限制非常讨厌,所以我们先忽略它的存在\ 令$Z=min(n,m)$
\begin{align*}
\text{令}g(i)=&\sum\left[(x,y)=i \right]\ \text{那么显然有}g(i)=&\sum_{i|d}\mu(\frac{d}{i})\lfloor \frac{n}{d}\rfloor \lfloor \frac{m}{d}\rfloor \ \because (i,j)=&d,d=p_1^{a_1}p_2^{a_2}\cdots p_k^{a_k}\ \text{由约数和定理得}F(d)=&(p_1^0+p_1^1+\cdots +p_1^{a_1})(p_2^0+p_2^1+\cdots +p_2^{a_2})\cdots (p_k^0+p_k^1+\cdots p_k^{a_k})\ \therefore F(pq)=&F(p)F(q),(p,q)=1\ \text{$F(i)$可}&\text{以利用线性筛在$O(n)$时间内处理出来,那么就有}\ ans=&\sum_{i=1}^{Z}F(i)g(i)\ =&\sum_{d=1}^{Z}\lfloor \frac{n}{d}\rfloor \lfloor \frac{m}{d}\rfloor \sum_{i|d}F(i)\mu(\frac{d}{i})
\end{align*}
然后我们可以发现最后这个式子的形式和上面非常像\ 然后利用上述前缀和和枚举除法取值的方法就可以完成\
\par 可是题目中还有一个$a$的限制,
我们可以发现对答案有贡献的只有$F(i)\leq a$$i$\ \par 可以离线来处理这个东西,
将询问按照$a$从小到大排序,将$F(i)$按照从小到大的顺序排序,
每次询问之前将所有$F(i)\leq a$插入并且用树状数组维护前缀和。
当然还有一个需要注意的问题是取模\footnote{取模可以利用自然溢出$int$最后再对$2^{31}-1$取与即可}\ 接连做了好几道题都有喜闻乐见的\textsl{除法分块\ref{2}}
\newpage
\begin{lstlisting}[language={[ANSI]C}]
#include<algorithm>
#include<iostream>
#include<cstdio>
#define N 100005
#define inf 0x7fffffff
#define ll long long
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int Q,mx,cnt;
struct data{
int n,m,a,id;
}q[N];
bool operator<(data a,data b){
return a.a<b.a;
}
pair<int,int>F[N];
int pri[N],mu[N];
int ans[N],t[N];
bool mark[N];
void add(int x,int val){
for(int i=x;i<=mx;i+=i&-i)t[i]+=val;
}
int query(int x){
int tmp=0;for(int i=x;i;i-=i&-i)tmp+=t[i];return tmp;
}
int Query(int l,int r){
return query(r)-query(l-1);
}
void getmu(){
mu[1]=1;
for(int i=2;i<=mx;i++){
if(!mark[i])pri[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&pri[j]*i<=mx;j++){
mark[pri[j]*i]=1;
if(i%pri[j]==0){mu[pri[j]*i]=0;break;}
else mu[pri[j]*i]=-mu[i];
}
}
for(int i=1;i<=mx;i++)
for(int j=i;j<=mx;j+=i)
F[j].first+=i;
for(int i=1;i<=mx;i++)F[i].second=i;
}
void solve(int x){
int id=q[x].id,n=q[x].n,m=q[x].m;
for(int i=1,j;i<=q[x].n;i=j+1){
j=min(n/(n/i),m/(m/i));
ans[id]+=(n/i)*(m/i)*Query(i,j);
}
}
int main(){
Q=read();
for(int i=1;i<=Q;i++){
q[i].n=read();q[i].m=read();q[i].a=read();
q[i].id=i;if(q[i].n>q[i].m)swap(q[i].n,q[i].m);
mx=max(mx,q[i].n);
}
getmu();int now=0;
sort(q+1,q+Q+1);sort(F+1,F+mx+1);
for(int i=1;i<=Q;i++){
while(now+1<=mx&&F[now+1].first<=q[i].a){
for(int j=F[++now].second;j<=mx;j+=F[now].second)
add(j,F[now].first*mu[j/F[now].second]);
}solve(i);
}
for(int i=1;i<=Q;i++)
printf("%d\n",ans[i]&inf);
return 0;
}
\end{lstlisting}
\subsubsection{例题5:}
\href{http://www.lydsy.com/JudgeOnline/problem.php?id=2154}{BZOJ 2154:Crash的数字表格}\据说和\href{https://www.luogu.org/problemnew/show/P3768}{\color{blue} "luogu P3768"}惊人的相似\\
\par 题目大意:给定$n,m$,求$$\sum_{i=1}^n\sum_{j=1}^m\left[ i,j\right] $$
\sout {感觉有点慌,完整的公式推导花了两页}
\subsubsection{例题6:}
\href{http://www.lydsy.com/JudgeOnline/problem.php?id=2693}{BZOJ 2693: jzptab}\据说和上题惊人的相似\\
\sout {就是上题加强了数据}
做法依旧很鬼畜
\end{document}