前言
因为笔者很菜,所以有部分结论没有证明。
积性函数
即为当 \(gcd(i,j)=1\),有 \(f(xy)=f(x)f(y)\),的函数 \(f(x)\),即为积性函数。
常见积性函数
- 莫比乌斯函数:\(\mu(x)= \left\{\begin{matrix} &1&,n=1 \\ &(-1)^k&, x=p_1 p_2 p_3 ... p_k,\forall p_i!=p_j,p_i\in prime\\ &0&,others \end{matrix}\right.\),第二个条件简单来说就是若 \(x\)分解质因数后每个分解出的素数的指数都\(\leq1\)
- 欧拉函数:\(\varphi(x)=\sum_{i=1}^{n}[gcd(i,n)=1]\)
- \(ID\)函数:\(ID(x)=x\)
- 单位函数:\(\varepsilon (x)=[x=1]\)
- 常数函数:\(1(x)=1\)
大部分积性函数都可以用线性筛求。
数论分块
即求 \(\sum_{i=1}^{n}\left \lfloor \frac{n}{i} \right \rfloor\),后面还可以带一些积性函数。这里就相当于乘上一个单位函数。暴力要 \(O(n)\),数论分块只需 \(\sqrt n\)。
\(Code:\)
inline void solve(int n){
register int ans=0;
for(register int l=1,r;l<=n;l=r+1){
r=n/(n/l);
ans+=(r-l+1)*(n/l);
}
printf("%d\n",ans);
}
狄利克雷卷积
也叫 \(Dirichlet\) 卷积
两个函数 \(a\),\(b\) 的狄利克雷卷积 \(f\) 为
其中
性质
- 交换律:\(f*g=g*f\)
- 结合律:\(f*(g*a)=(f*g)*a\)
- 任意数论函数与单位函数的卷积为其本身:\(f*\varepsilon=f\)
莫比乌斯函数
\(\mu(x)= \left\{\begin{matrix} &1&,n=1 \\ &(-1)^k&, x=p_1 p_2 p_3 ... p_k,\forall p_i!=p_j,p_i\in prime\\ &0&,others \end{matrix}\right.\)
线性筛求莫比乌斯函数
与筛 \(\varphi\) 函数的写法差不多。
inline void Gmu(){
mu[1]=1;tot=0;
for(register int i=2;i<=MAX;++i){
if(!f[i]){
p[++tot]=i;
mu[i]=-1;
}
for(register int j=1;j<=tot&&i*p[j]<=MAX;++j){
f[i*p[j]]=1;
if(i%p[j]==0){
mu[i*p[j]]=0;
break;
}
mu[i*p[j]]=-mu[i];
}
}
for(register int i=1;i<=MAX;++i)mu[i]+=mu[i-1];
}
性质
-
\[\sum_{d|n}\mu(d)=[n=1] \]
证明:
当 \(n=1\) 时,显然成立
当 \(n\ne1\)时,将 \(n\) 分解质因数,\(n=p_1^{c_1}p_2^{c_2}p_3^{c_3}... \forall p_i!=p_j,p_i\in prime\)
可以发现对于所有 \(d\),只有当 \(d\) 的所有质因子的次数为 \(1\) 时,\(\mu(d) \ne 0\),而质因子为 \(m\) 的因子为 \(\binom{n}{m}\),易得
后面就是二项式定理 \((x+y)^n=\sum_{i=0}^{n} \binom{n}{i}x^{n}y^{n-i}\),带入 \(x=1,y=-1\),得:
所以
-
\[\sum_{d|n}\frac{\mu(d)}{d}=\frac{\varphi(n)}{n} \]
将 \(ID\) 和 \(\varphi\) 带入 莫比乌斯反演的公式即可
莫比乌斯反演
若有数论函数 \(f,g\),满足
则
证明:
把条件转为狄利克雷卷积的形式,即为
两边同时卷 \(\mu\)
因为满足结合律,\(1*\mu=\varepsilon\)
则 \(f*\mu=g* \varepsilon\)
有因为任何函数卷 \(\varepsilon\) 都为本身,所以
这个卷积的式子就等价于
例题
-
luogu P3455【[POI2007]ZAP-Queries】
\(\texttt{blog食用}\)
首先可以看出题目求
首先很套路地转化为
有一个数论函数 \(\varepsilon(x)\),当\(x=1\)时,函数值为\(1\)否则为\(0\)
后面的\([gcd(i,j)=1]\)可以用\(\varepsilon(gcd(i,j))\)
用 \(\mu*1 = \varepsilon\) 反演
改变求和顺序
后面两个\(\sum\),可以看出求的是\(d\)有多少个倍数在\(\left \lfloor \frac{a}{d} \right\rfloor\)和\(\left \lfloor \frac{b}{d} \right \rfloor\)中。显然,答案分别为\(\left \lfloor \frac{a}{pd} \right\rfloor\)和
\(\left \lfloor \frac{b}{pd} \right\rfloor\)
转化为
然后数论分块加前缀和就做完了
我Code里的是先把\(a/d,b/d\),再做数论分块,这样常数会小一点。
$ Code $
#include
#include
#include
using namespace std;
#define int long long
inline int read(){
register int x=0,f=0,ch=getchar();
while('0'>ch||ch>'9')f^=ch=='-',ch=getchar();
while('0'<=ch&&ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar();
return f?-x:x;
}
const int MAX=50100;
int mu[MAX],pri[MAX],f[MAX],tot;
inline void Getmu(int N){
mu[1]=1;
for(register int i=2;i<=N;++i){
if(!f[i]){
mu[i]=-1;
pri[++tot]=i;
}
for(register int j=1;j<=tot&&i*pri[j]<=N;++j){
f[i*pri[j]]=1;
if(i%pri[j]==0)break;
mu[i*pri[j]]=-mu[i];
}
}
for(register int i=1;i<=N;++i)mu[i]+=mu[i-1];
}
int ans;
inline int solve(int n,int m){
ans=0;
if(n>m)n^=m^=n^=m;
for(register int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
ans+=(mu[r]-mu[l-1])*(n/l)*(m/l);
}
return ans;
}
int t,a,b,d;
signed main(){
Getmu(50000);
t=read();
while(t--){
a=read(),b=read(),d=read();
a/=d,b/=d;
printf("%lld\n",solve(a,b));
}
return 0;
}
-
luogu P2398 【GCD SUM】
\(\texttt{blog食用}\)
题目要求
然后再去重。
可以想到枚举\(n\)以内的\(p\),把对答案的贡献改成\(p\times gcd(i,j)=p\)的数的个数
化简式子
因为\([gcd(i,j)=1]\)只有\(gcd(i,j)=1\)有\(1\)的贡献,否则没有,所以可以替换为\(\varepsilon(gcd(i,j))\)
\(\varepsilon(x)\)即为当\(x=1\)时对答案贡献为\(1\),否则为\(0\)
所以现在转化为了
由\(Dirichlet\)卷积得
\(\varepsilon =\mu*1 \Leftrightarrow \varepsilon(n)=\sum_{d|n}\mu(d)\)
所以原式转化为
转化一下求和顺序
后面两个即为求\(\left \lfloor \frac{n}{p} \right \rfloor\)中\(d\)的倍数,易知其答案为\(\left \lfloor \frac{n}{pd} \right \rfloor\)
所以答案转化为
后面那玩意用数论分块和前缀和即可。
\(Code\):
#include
#include
#include
using namespace std;
#define int long long
inline int read(){
register int x=0,f=0,ch=getchar();
while('0'>ch||ch>'9')f^=ch=='-',ch=getchar();
while('0'<=ch&&ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar();
return f?-x:x;
}
const int MAX=100005;
int n,tot,p[MAX],f[MAX],mu[MAX];
inline void Gmu(){
mu[1]=1;tot=0;
for(register int i=2;i<=n;++i){
if(!f[i]){
p[++tot]=i;
mu[i]=-1;
}
for(register int j=1;j<=tot&&i*p[j]<=n;++j){
f[i*p[j]]=1;
if(i%p[j]==0){
mu[i*p[j]]=0;
break;
}
mu[i*p[j]]=-mu[i];
}
}
for(register int i=1;i<=n;++i)mu[i]+=mu[i-1];
}
int res,ans;
inline void solve(int n,int p){
res=0;
for(register int l=1,r;l<=n;l=r+1){
r=n/(n/l);
res+=(mu[r]-mu[l-1])*(n/l)*(n/l);
}
ans+=res*p;
}
signed main(){
n=read();
Gmu();
for(register int p=1;p<=n;++p)solve(n/p,p);
printf("%lld\n",ans);
return 0;
}
参考
oi-wiki、度娘