莫比乌斯反演
前言
莫比乌斯反演是数论中的重要内容。对于一些函数$f(n)$,如果很难直接求出它的值,而容易求出其$g(n)$(倍数和或约数和),那么可以通过莫比乌斯反演简化运算,求得$f(n)$的值。
开始学习莫比乌斯反演前,我们需要一些前置知识:整除分块、积性函数、Dirichlet 卷积、莫比乌斯函数。
太毒瘤了
整除分块
对于形似$\sum_{k}^{n} \lfloor\frac{n}{k}\rfloor \times ……$的表达式,显而易见,$\forall x = \lfloor\frac{n}{k}\rfloor存在[l,r](l<=r)且\forall i\in [l,r]满足x=\lfloor\frac{n}{i}\rfloor$,那么可以根据$x$将表达式分块计算,并且$\forall i,R_{i}=L_{i+1}-1$,可以用以下代码迅速处理出。时间复杂度$O(\sqrt{n})$。
for(int i=1,r;i<=n;i=r+1)
{
r=_min(n/(n/i),m/(m/i));
}
积性函数
定义:$gcd(a,b)=1$且$f(ab)=f(a)f(b)$,则函数$f$为积性函数。
性质:若$f(x)$和$g(x)$为积性函数,则以下函数均为积性函数:
$$\begin{eqnarray}h(x) & & = & f\left(x^{p}\right) \\h(x) & & = & f^{p}(x) \\h(x) & & = & f(x) g(x) \\h(x) & & = & \sum_{d \mid x} f(d) g\left(\frac{x}{d}\right)\end{eqnarray}$$
积性函数例子:
$$\begin{array}{l} \text{约数个数函数}\qquad d(n) = \sum_{d \mid n} 1 &\\ \text{约数和函数}\qquad \sigma(n) = \sum_{d \mid n} d &\\ \text{约数k次幂函数}\qquad \sigma_{k}(n) = \sum_{d \mid n} d^{k} &\\ \text{欧拉函数}\qquad \varphi(n) = \sum_{i = 1}^{n}[\operatorname{gcd}(i, n) = 1] &\\ \text{莫比乌斯函数}\qquad \mu(n) = \left\{ \begin{eqnarray} 1 & n = 1 &\\ (-1)^{k} & c_{1,2, \cdots, k} = 1 \quad\left(n = \prod_{i = 1}^{k} p_{i}^{c_{i}}\right)&\\ 0 & c_{i}>1 \end{eqnarray}\right. \end{array}$$
Dirichlet 卷积
又称狄利克雷卷积,实质上是一种定义新运算。\begin{eqnarray}f*g(n)=\sum_{d|n}^{n}f(d)g(\frac{n}{d} )\end{eqnarray}.*为运算符卷。满足交换结合律分配。
莫比乌斯函数
简化定义:
$$\mu(n) = \left\{ \begin{eqnarray} 1 & n = 1 &\\ (-1)^{k} & \qquad k为n不同质因子个数&\\0 & n含有平方因子 \end{eqnarray}\right.$$
公式
$$\begin{eqnarray}\varepsilon = \mu * 1 \Longleftrightarrow \varepsilon(n) = \sum_{d \mid n} \mu(d) \\d = 1 * 1 \Longleftrightarrow d(n) = \sum_{d \mid n} 1 \\\sigma = \mathrm{id} * 1 \Longleftrightarrow \sigma(n) = \sum_{d \mid n} d \\\varphi = \mu * \mathrm{id} \Longleftrightarrow \varphi(n) = \sum_{d \mid n} d \cdot \mu\left(\frac{n}{d}\right) \\f(n)=\sum_{d n} g(d) \Longleftrightarrow g(n)=\sum_{n \mid d} f(d) \mu\left(\frac{d}{n}\right) \\\sum_{d \mid n} f(n) \mu\left(\frac{n}{d}\right)=\sum_{d \mid n}\left(\sum_{k \mid d} g(k)\right) \mu\left(\frac{n}{d}\right) \\\end{eqnarray}$$
证明
咕咕咕
例题
$$\begin{eqnarray} &\sum_{i = 1}^{n}\sum _{j = 1}^{n}\gcd (i,j)~~~~~~~~~~~~~~~~~~\\& =\sum_{k=1}^{n}k\sum_{i = 1}^{n}\sum_{j = 1}^{n}\left [\gcd(i,j)=k \right ]\\& =\sum_{k=1}^{n}k\sum_{i=1}^{\frac{n}{k} }\sum_{j = 1}^{\frac{n}{k} }\left [\gcd(i,j)=1 \right ]\\& =\sum_{k=1}^{n}k\sum_{i=1}^{\frac{n}{k} }\sum_{j = 1}^{\frac{n}{k} }\varepsilon \left (\gcd(i,j)\right)~~~~\\& \because \varepsilon(n)=\sum_{d|n}\mu(d)~~~~~~~~~~~~~~~~~~~~\\& \therefore \varepsilon\left (\gcd(i,j)\right)=\sum_{d|\left(\gcd(i,j)\right)}\mu(d)\\& =\sum_{k=1}^{n}k\sum_{i=1}^{\frac{n}{k} }\sum_{j=1}^{\frac{n}{k}}\sum_{d|\gcd(i,j)}\mu(d)\\& =\sum_{k=1}^{n}k\sum_{d=1}^{n}\mu(d)\sum_{i=1}^{\frac{n}{k}}[d|i]\sum_{j=1}^{\frac{n}{k}} [d|j]\\& \because \sum_{j=1}^{\frac{n}{k}}[d|j]=\lfloor\frac{n}{kd}\rfloor \\& \sum_{i=1}^{\frac {n}{k}}[d|j]=\lfloor\frac{n}{kd}\rfloor\\& \therefore 原式=\sum_{k=1}^{n}k\sum_{d=1}^{n}\mu(d)\lfloor\frac{n}{kd}\rfloor\cdot \lfloor \frac{n}{kd}\rfloor\\& T{\color{Red}->} kd\\& =\sum_{T=1}^{n}\sum_{k|T}k\mu(\frac{T}{k} )\lfloor\frac{n}{T}\rfloor\lfloor\frac{n}{T}\rfloor\\& \because id*\mu(T)=\sum_{k|T}id(k)\mu(\frac{T}{k})=\sum_{k|T}k\mu(\frac{T}{k})\\& \therefore 原式=\sum_{T=1}^{n}\times (id*\mu(T))\lfloor\frac{n}{T}\rfloor\lfloor\frac{n}{T}\rfloor\\& \because id*\mu(T)=\varphi (T)\\& \therefore 原式=\sum_{T=1}^{n}\varphi (T)\lfloor\frac{n}{T}\rfloor\lfloor\frac{n} {T}\rfloor\\& \end{eqnarray}$$
所以可以整除分块算出值,然后减去,再去掉重复的gcd(i,j)和gcd(j,i),即除以2
#include<bits/stdc++.h> using namespace std; const int N=5000000; int cnt,zhi[N]; bool he[N]; long long p[N],ans,sum[N],n; int main() { p[1]=1; scanf("%lld",&n); for(long long i=2;i<=n;i++) { if(he[i]==0) { p[i]=i-1; zhi[++cnt]=i; } for(int j=1;j<=cnt&&i*zhi[j]<=n;j++) { he[i*zhi[j]]=true; if(i%zhi[j]==0) { p[i*zhi[j]]=p[i]*zhi[j]; break; } else { p[i*zhi[j]]=p[i]*p[zhi[j]]; } } } for(int i=1;i<=n;i++) sum[i]+=sum[i-1]+p[i]; //整除分块 for(int i=1,r;i<=n;i=r+1) { r=n/(n/i); ans+=(sum[r]-sum[i-1])*(n/i)*(n/i); } ans-=n*(n+1)/2; ans/=2; printf("%lld\n",ans); return 0; }
2.YY的GCD
$$\begin{eqnarray} \sum_{i=1}^{n}\sum_{j=1}^{j}[\gcd(i,j)==prime] &\\ \sum_{d=1}^{n}[d=prime]\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{m}{d}}[\gcd(i,j)=1] &\\ \sum_{d=1}^{n}[d=prime]\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{m}{d}}\varepsilon(\gcd(i,j)) &\\ \sum_{d=1}^{n}[d=prime]\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{m}{d}}\sum_{k|gcd(i,j)}*\mu(k) &\\ \sum_{d=1}^{n}[d=prime]\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{m}{d}}\sum_{k|i,k|j}*\mu(k) &\\\because i,j为k的倍数 &\\ \therefore 原式=\sum_{d=1}^{n}[d=prime]\sum_{k=1}^{\frac{n}{d}}*\mu(k)\lfloor\frac{n}{kd}\rfloor \lfloor\frac{m}{kd}\rfloor &\\{\color{Red} T->kd} 则原式=\sum_{T=1}^{n}\lfloor\frac{n}{T}\rfloor \lfloor\frac{m}{T}\rfloor \sum_{d|T}[d=prime]*\mu(\frac{T}{d}) &\\\end{eqnarray}$$
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define LL long long LL mu[10000010];int flag[10000010],prime[10000010],cnt,f[10000010],sum[10000010]; void sieve() { mu[1]=1; for (int i=2;i<=10000000;i++) { if (!flag[i]) prime[++cnt]=i,mu[i]=-1; for (int j=1;j<=cnt&&i*prime[j]<=10000000;j++) { flag[i*prime[j]]=1; if (i%prime[j]==0) break; mu[i*prime[j]]=-mu[i]; } } for (int i=1;i<=cnt;i++) for (int j=1;prime[i]*j<=10000000;j++) f[j*prime[i]]+=mu[j]; for (int i=1;i<=10000000;i++) sum[i]=sum[i-1]+f[i]; } LL solve(int a,int b) { LL ans=0; if (a>b) swap(a,b); for (int l=1,r=0;l<=a;l=r+1) { r=min(a/(a/l),b/(b/l)); ans+=(LL)(sum[r]-sum[l-1])*(LL)(a/l)*(LL)(b/l); } return ans; } int main() { sieve(); int n,m,T;scanf("%d",&T); while (T--) { scanf("%d%d",&n,&m); if (n>m) swap(n,m); printf("%lld\n",solve(n,m)); } }
3.约数个数和
$$\begin{eqnarray}\sum_{i=1}^{n}\sum_{j=1}^{m}d(ij) &\\d(ij)=\sum_{x|i}\sum_{y|j}\sum_{d|\gcd(i,j)}\mu(d) &\\=\sum_{d|i,d|j}\mu(d)\sum_{x|\frac{i}{d}}\sum_{x|\frac{j}{d}}1 &\\=\sum_{d|i,d|j}\mu(d)d(\frac{i}{d})d(\frac{j}{d}) &\\原式=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|i,d|j}\mu(d)d(\frac{i}{d})d(\frac{j}{d}) &\\=\sum_{d=1}^{n}\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{m}{d}}\mu(d)d(i)d(j) &\\=\sum_{d=1}^{n}\mu(d)\sum_{i=1}^{\frac{n}{d}}d(i)\sum_{j=1}^{\frac{m}{d}}d(j) &\\\end{eqnarray}$$
#include<bits/stdc++.h> using namespace std; const int N=50005; inline int read(){ int s=0,w=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();} while(isdigit(ch)){s=(s<<1)+(s<<3)+ch-'0';ch=getchar();} return s*w; } inline int _min(int a,int b){ return a<b?a:b; } int n,m,T,zhi[N],d[N],mu[N],cnt,t[N]; long long ans; bool he[N]; void deal(){ for(int i=1,r;i<=n;i=r+1) r=_min(n/(n/i),m/(m/i)),ans+=1ll*d[n/i]*d[m/i]*(mu[r]-mu[i-1]); } int main(){ mu[1]=d[1]=1;he[0]=he[1]=true; for(int i=2;i<N;i++){ if(he[i]==0){ zhi[++cnt]=i; mu[i]=-1; d[i]=2; t[i]=1; } for(int j=1;j<=cnt&&zhi[j]*i<N;j++){ he[i*zhi[j]]=true; if(i%zhi[j]==0){ d[i*zhi[j]]=d[i]/(t[i]+1)*(t[i]+2); t[i*zhi[j]]=t[i]+1; break; } else{ d[i*zhi[j]]=d[i]*2; t[i*zhi[j]]=1; mu[i*zhi[j]]=-mu[i]; } } } for(int i=2;i<N;i++){ mu[i]+=mu[i-1]; d[i]+=d[i-1]; } T=read(); while(T--){ ans=0; n=read();m=read(); if(n>m) swap(n,m); deal(); printf("%lld\n",ans); } return 0; }
4.数字表格
(图源SpadeA261)
#include<bits/stdc++.h> #define int long long using namespace std; const int N=1000005; int n,m,mu[N],zhi[N],cnt; bool he[N]; long long sum[N],ans,f[N],mod=1e9+7,sum_inv[N],f_inv[N]; int _max(int a,int b) { return a>b?a:b; } int _min(int a,int b) { return a<b?a:b; } inline int read() { int s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while('0'<=ch&&ch<='9'){s=(s<<1)+(s<<3)+ch-'0';ch=getchar();} return s*w; } long long quick_pow(long long a,long long b) { long long rec=1; while(b>0) { if((b&1)==1) { rec=rec*a%mod; } a=a*a%mod; b>>=1; } return rec; } long long inv(long long x) { if(x==0) return 1; return quick_pow(x,mod-2); } void deal() { for(int i=1,r;i<=n;i=r+1) { r=_min(n/(n/i),m/(m/i)); ans*=quick_pow(sum[r]*sum_inv[i-1]%mod,(n/i)*(m/i)%(mod-1)); ans%=mod; } return; } signed main() { int T=read(); f_inv[1]=sum_inv[0]=sum_inv[1]=sum[1]=sum[0]=f[1]=mu[1]=1; for(int i=2;i<=N;i++) { f[i]=(f[i-1]+f[i-2])%mod; f_inv[i]=inv(f[i]); sum[i]=1; if(he[i]==0) { zhi[++cnt]=i; mu[i]=-1; } for(int j=1;j<=cnt&&zhi[j]*i<=N;j++) { he[i*zhi[j]]=true; if(i%zhi[j]==0) { mu[i*zhi[j]]=0; break; } else { mu[i*zhi[j]]=-mu[i]; } } } for(int i=1;i<=N;i++) { if(mu[i]==0) continue; for(int j=1;i*j<=N;j++) { sum[i*j]=sum[i*j]*(mu[i]==1?f[j]:f_inv[j])%mod; } } for(int i=2;i<=N;i++) { sum[i]=sum[i]*sum[i-1]%mod; sum_inv[i]=inv(sum[i]); } while(T--) { ans=1; n=read();m=read(); if(n>m) swap(n,m); deal(); printf("%lld\n",((ans+mod)%mod+mod)%mod); } return 0; }
心得
太毒瘤了吧,果然信息最难的题是数学题。
补题去了~~~