【数论】莫比乌斯反演 例题归纳
指路博客:peng-ym 莫比乌斯反演
莫比乌斯函数:
·当 \(d=1\)时,\(\mu(d)=1\);
·当\(d=\prod_{i=1}^k p_i\)且\(p_i\)为互异素数时,\(\mu(d)=(-1)^k\)。
·只要当\(d\)含有任何质因子的幂次大于等于2,则函数值为0。
·对于任意正整数\(n\),\(\sum_{d|n}^{} \mu(d)=[n = 1]\)。
·对于任意正整数\(n\),\(\sum_{d|n}^{} {\frac{\mu(d)}d}={\frac{\phi(n)}n}\)。
·线筛莫比乌斯函数(顺带欧拉函数:
int pri[maxn+5],mu[maxn+5],phi[maxn];
void init()
{
mu[1]=phi[1]=1;
for(int i=2;i<maxn;i++)
{
if(!pri[i])
{
pri[++pri[0]]=i;phi[i]=i-1;mu[i]=-1;
}
for(int j=1;j<=pri[0]&&i*pri[j]<maxn;j++)
{
pri[i*pri[j]]=1;
if(i%pri[j]==0)
{
phi[i*pri[j]]=phi[i]*pri[j];
break;
}
phi[i*pri[j]]=phi[i]*(pri[j]-1);
mu[i*pri[j]]=-mu[i];
}
}
}
莫比乌斯反演
·F(n)和f(n)是定义在非负整数集合上的两个函数,并且满足条件:$$F(n)=\sum_{d|n}f(d)$$
那么存在一个结论:$$f(n)=\sum_{d|n}μ(d)F(\lfloor\frac nd\rfloor)$$
这个定理就称作莫比乌斯反演定理。
·莫比乌斯反演有另外的一种形式,当F(n)和f(n)满足:$$F(n)=\sum_{n|d}f(d)$$
可以推出:$$f(n)=\sum_{n|d}μ(\frac dn)F(d)$$
例题
-
题目描述:
给定N, M,求1<=x<=N, 1<=y<=M且 gcd(x, y)为质数的(x, y)有多少对
-
输入格式:
第一行一个整数T 表述数据组数
接下来T行,每行两个正整数,表示N, M
-
数据范围:
T = 10000;N, M <= 10000000
-
输出格式:
T行,每行一个整数
指路博客:peng-ym YY的GCD
-
等效: \(Ans=\sum_{i=1}^n \sum_{j=1}^m [gcd(x,y)=prim]\)
-
设\(f(d)\)为\(gcd(i,j)=d\)的个数,\(F(n)\)为 \(gcd(i,j)=n\)和\(n\)的倍数 的个数,即:
\[f(d)=\sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)=d] \]\[F(n)=\sum_{n|d} f(d)=\lfloor\frac Nn\rfloor\lfloor\frac Mn\rfloor \]由莫比乌斯反演:
\[f(n)=\sum_{n|d}\mu(\lfloor\frac dn\rfloor)F(d) \] -
化简公式:
\[Ans=\sum_{p\in prim} \sum_{i=1}^n \sum_{j=1}^m[gcd(i,j)=p] \]把\(f(p)\)代入得:
\[\begin{align*} Ans & =\sum_{p\in prim}f(p)\\ & =\sum_{p\in prim}\sum_{n|d}\mu(\lfloor\frac dn\rfloor)F(d) \end{align*} \]换一个枚举项,枚举\(\lfloor \frac dp\rfloor\):
\[\begin{align*} Ans & =\sum_{p\in prim}\sum_{d=1}^{min(\lfloor \frac np\rfloor,\lfloor \frac mp\rfloor)}\mu(d)F(dp)\\ & =\sum_{p\in prim}\sum_{d=1}^{min(\lfloor \frac np\rfloor,\lfloor \frac mp\rfloor)} \mu(d)\lfloor \frac np\rfloor\lfloor \frac mp\rfloor \end{align*} \]将dp换成T:
\[\begin{align*} Ans & =\sum_{T=1}^{min(n,m)}\sum_{t|T,t\in prim}\mu(\lfloor \frac Tt\rfloor)\lfloor\frac nT\rfloor\lfloor \frac mT\rfloor\\ & =\sum_{T=1}^{min(n,m)}\lfloor\frac nT\rfloor\lfloor \frac mT\rfloor(\sum_{t|T,t\in prim}\mu(\lfloor \frac Tt\rfloor)\;) \end{align*} \] -
之后整除分块
-
代码:
#include<bits/stdc++.h> #define debug1 printf("!"); #define debug2 puts("#"); using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn=1e7; const int mod=6893911; const int inf=0x3f3f3f3f; template<typename T>inline void read(T&x) { char c; while(!isdigit(c=getchar()));x=c-'0'; while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-'0'; } ll sum[maxn+5]; int mu_pfac[maxn+5]; int mu[maxn+5]; int pri[maxn+5],cnt; inline void init() { mu[1]=1; for(int i=2;i<=maxn;i++) { if(!pri[i]) { pri[++cnt]=i; mu[i]=-1; } for(int j=1;j<=cnt&&i*pri[j]<=maxn;j++) { pri[i*pri[j]]=1; if(i%pri[j]==0)break; mu[i*pri[j]]=-mu[i]; } } for(int i=1;i<=maxn;i++) for(int j=1;j<=cnt&&i*pri[j]<=maxn;j++) mu_pfac[i*pri[j]]+=mu[i]; for(int i=1;i<=maxn;i++) sum[i]=sum[i-1]+(ll)mu_pfac[i]; } inline ll solve(int n,int m) { if(n>m)swap(n,m); ll ans=0; for(int l=1,r;l<=n;l=r+1) { r=min(n/(n/l),m/(m/l)); ans+=1ll*(n/l)*(m/l)*(sum[r]-sum[l-1]); } return ans; } int main() { init(); int T; read(T); while(T--) { int n,m; read(n);read(m); printf("%lld\n",solve(n,m)); } }
-
题目描述:
给定a, b,d,求1<=x<=a, 1<=y<=b且 gcd(x, y)=d的(x, y)有多少对
-
输入格式:
第一行一个整数T 表述数据组数
接下来T行,每行三个正整数,表示a,b,d
-
数据范围:
\(1\leq T\leq 5\times 10^4,1\leq d\leq a,b\leq 5\times 10^4\)
-
输出格式:
T行,每行一个整数
指路博客:peng-ym ZAP-Queries
-
等效: \(Ans=\sum_{i=1}^a \sum_{j=1}^b [gcd(x,y)=d]\)
-
设\(f(d)\)为\(gcd(i,j)=d\)的个数,\(F(n)\)为 \(gcd(i,j)=n\)和\(n\)的倍数 的个数,即:
\[f(d)=\sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)=d] \]\[F(n)=\sum_{n|d} f(d)=\lfloor\frac an\rfloor\lfloor\frac bn\rfloor \]由莫比乌斯反演:
\[f(n)=\sum_{n|d}\mu(\lfloor\frac dn\rfloor)F(d) \]为了不混淆,将题意的d换成n,把\(f(n)\)代入得:
\[\begin{align*}Ans & =f(n)\\ & =\sum_{n|d}\mu(\lfloor\frac dn\rfloor)F(d) \end{align*} \]换一个枚举项,枚举\(\lfloor \frac dn\rfloor\),设其为\(t\):
\[Ans =\sum_{t=1}^{min(\lfloor \frac ap\rfloor,\lfloor \frac bd\rfloor)}\mu(t)\lfloor \frac a{td}\rfloor\lfloor \frac b{td}\rfloor \] -
之后整除分块
-
代码:
#include<bits/stdc++.h> #define debug1 printf("!"); #define debug2 puts("#"); using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn=1e5+5; const int mod=6893911; const int inf=0x3f3f3f3f; template<typename T>inline void read(T&x) { char c; while(!isdigit(c=getchar()));x=c-'0'; while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-'0'; } ll smu[maxn+5]; int mu[maxn+5]; int pri[maxn+5],cnt; inline void init() { mu[1]=1; for(int i=2;i<=maxn;i++) { if(!pri[i]) { pri[++cnt]=i; mu[i]=-1; } for(int j=1;j<=cnt&&i*pri[j]<=maxn;j++) { pri[i*pri[j]]=1; if(i%pri[j]==0)break; mu[i*pri[j]]=-mu[i]; } } for(int i=1;i<=maxn;i++)smu[i]=smu[i-1]+(ll)mu[i]; } inline ll solve(int a,int b,int d) { if(a>b)swap(a,b); ll ans=0; for(int l=1,r;l<=a;l=r+1) { r=min(a/(a/l),b/(b/l)); ans+=1ll*(a/(l*d))*(b/(l*d))*(smu[r]-smu[l-1]); } return ans; } int main() { init(); int T; read(T); while(T--) { int a,b,d; read(a);read(b);read(d); printf("%lld\n",solve(a,b,d)); } }
-
题目描述:
设 d(x) 为 x 的约数个数,给定 n,m,求
\[\sum_{i=1}^n\sum_{j=1}^m d(ij) \] -
输入格式:
第一行一个整数T 表述数据组数
接下来T行,每行两个正整数 n,m
-
数据范围:
\(1\leq T,n,m\leq 5\times 10^4\)
-
输出格式:
T行,每行一个整数
指路博客:peng-ym 约数个数和
-
重要性质:
\[d(ij)=\sum_{x|i}\sum_{y|i}[gcd(x,y)=1] \] -
等效\(Ans=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|i}[gcd(x,y)=1]\)
-
设\(f(d)\)为\(gcd(i,j)=d\)的个数,\(F(n)\)为 \(gcd(i,j)=n\)和\(n\)的倍数 的个数,即:
\[f(d)=\sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)=d] \]\[F(n)=\sum_{n|d} f(d)=\lfloor\frac Nn\rfloor\lfloor\frac Mn\rfloor \]由莫比乌斯反演:
\[f(n)=\sum_{n|d}\mu(\lfloor\frac dn\rfloor)F(d) \] -
于是有:
\[\begin{align*} Ans &=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|i}[gcd(x,y)=1]\\ &=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|i}\sum_{d|gcd(x,y)}\mu(d)\\ &=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|i}\sum_{d=1}^{min(n,m)}\mu(d)*[d|gcd(x,y)]\;\;\;(改枚举d)\\ &=\sum_{d=1}^{min(n,m)}\mu(d)\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|i}[d|gcd(x,y)]\;\;\;(将\mu(d)提出来)\\ \end{align*} \]由枚举i,j和它们的约数改变为枚举它们的约数再直接乘上这些约数的倍数的个数。因为每一个约数都会对它的倍数产生贡献:
\[\begin{align*} Ans&=\sum_{d=1}^{min(n,m)}\mu(d)\sum_{x=1}^n\sum_{y=1}^m[d|gcd(x,y)]\lfloor\frac nx\rfloor\lfloor\frac my\rfloor\\ &=\sum_{d=1}^{min(n,m)}\mu(d)\sum_{x=1}^{\lfloor\frac nd\rfloor}\sum_{y=1}^{\lfloor\frac md\rfloor}\lfloor\frac n{dx}\rfloor\lfloor\frac m{dy}\rfloor\;\;\;(将枚举x,y换成枚举dx,dy)\\ &=\sum_{d=1}^{min(n,m)}\mu(d)(\sum_{x=1}^{\lfloor\frac nd\rfloor}\lfloor\frac n{dx}\rfloor)(\sum_{y=1}^{\lfloor\frac md\rfloor}\lfloor\frac m{dy}\rfloor)\;\;\;(\lfloor\frac n{dx}\rfloor与y无关,所以可以提前)\\ \end{align*} \] -
之后整除分块
-
代码:
#include<bits/stdc++.h> #define debug printf("#"); #define pb push_back using namespace std; typedef long long ll; const int mod=998244353; const int maxn=5e4+5; const int inf=0x3f3f3f3f; ll s[maxn]; //ll phi[maxn+5]; int mu[maxn+5]; int pri[maxn],p=0; bool isp[maxn+5]={0}; inline void init() { // phi[1]=1; mu[1]=1; for(int i=2;i<=maxn;i++) { if(!isp[i]) { pri[++p]=i; // phi[i]=i-1; mu[i]=-1; } for(int j=1;j<=p&&i*pri[j]<=maxn;j++) { isp[i*pri[j]]=1; if(i%pri[j]==0) { // phi[i*pri[j]]=phi[i]*pri[j]; break; } // phi[i*pri[j]]=phi[i]*(pri[j]-1); mu[i*pri[j]]=-mu[i]; } } for(int i=2;i<=maxn;i++) { // phi[i]+=phi[i-1]; mu[i]+=mu[i-1]; } for(int i=1;i<=50000;i++) { for(int l=1,r;l<=i;l=r+1) { r=i/(i/l); s[i]+=(r-l+1)*(i/l); } } } int main() { init(); int T; scanf("%d",&T); while(T--) { int n,m,up; ll ans=0; scanf("%d%d",&n,&m); up=min(n,m); for(ll l=1,r;l<=up;l=r+1) { r=min(n/(n/l),m/(m/l)); ans+=1ll*(mu[r]-mu[l-1])*s[n/l]*s[m/l]; } printf("%lld\n",ans); } }