莫比乌斯反演
莫比乌斯函数
定义
定义莫比乌斯函数
即:在
在
性质
-
莫比乌斯函数是积性函数
即:对于任意互质的整数
,都有 ,利用这个性质,可以利用欧拉筛在 的时间内求出 ,并可用杜教筛快速计算 -
莫比乌斯函数
在迪利克雷卷积中为常数函数 的逆元。即:对任意数论函数
、 ,若有 则有 ,这里数论函数 $g(n) 即为数论函数 的莫比乌斯反演。
重要结论
即
证明:由性质 2 可以推出
-
证明:由结论1可得,
,当且仅当 时,其值为
莫比乌斯函数求值
莫比乌斯反演一般会用到莫比乌斯函数的前缀和,下面提供两种计算方法,各有优劣。
欧拉筛
欧拉筛适用于对积性函数求值,上文提到
通过欧拉筛求积性函数的值,需要确定
-
当
为质数时,根据定义可得 -
当
且 时,一定有 ,根据定义, -
当
且 时,若 含有平方因子,则 ;若 不含平方因子,根据定义可知: 。综上,当 时,
由此可以写出线性时间求解莫比乌斯函数的代码:
inline void Euler(int n) {
mu[1]=1; //根据定义得
for(int i=2;i<=n;i++) {
if(!vis[i]) mu[i]=-1,pr[++pcnt]=i; //情况1:质数
for(int j=1;j<=pcnt&&i*pr[j]<=n;j++) {
vis[i*pr[j]]=true;
if(i%pr[j]==0) {
mu[i*pr[j]]=0;break; //情况2:相同质因子
}
mu[i*pr[j]]=-mu[i]; //情况3:新的不同质因子
}
}
}
杜教筛
有时题目要求快速计算
杜教筛要求找出一个
定义
根据杜教筛的式子
直接递归计算的时间复杂度为
代码:
unordered_map<long long,long long>mp1; //map是O(logn)查询修改,选用unordered_map可以获得更优的O(1)查询修改
long long SumMu(long long n) { //先调用 Euler(MAXN-1) 预处理前 MAXN 项的 sumMu
if(n<MAXN) return sumM[n]; //如果当前的n在预处理的范围内,就直接返回
if(mp.find(n)!=mp.end()) return mp[n]; //如果当前的值之前求过,就直接返回
long long res=1; //epsilon前缀和=1
for(long long i=2;i<=n;i++) { //数论分块
int j=n/(n/i);
res-=1ll*(j-i+1)*SumMu(n/i);
i=j;
}
mp[n]=res; //记忆化加速下次查询
return res;
}
应用
[POI2007]ZAP-Queries
给出
上文证明
令
题目要求的式子就等于
回答询问代码:
inline int calc(int n,int m) {
if(n>m) swap(n,m);
int res=0;
for(int i=1;i<=n;i++) { //数论分块
int j=min(n/(n/i),m/(m/i));
res+=(sum[j]-sum[i-1])*(n/i)*(m/i); //sum为mu的前缀和
i=j;
}
return res;
}
PGCD - Primes in GCD Table
给定
即是求:
这里是两层数论分块的嵌套,回答一次询问的时间复杂度为
回答询问代码:
inline long long f(int n,int m) { //数论分块求f(n,m)
if(n>m) swap(n,m);
long long res=0;
for(int i=1;i<=n;i++) {
int j=min(n/(n/i),m/(m/i));
res+=1ll*(smu[j]-smu[i-1])*(n/i)*(m/i);
i=j;
}
return res;
}
inline long long calc(int n,int m) {
if(n>m) swap(n,m);
long long res=0;
for(int i=1;i<=n;i++) {
int j=min(n/(n/i),m/(m/i));
res+=1ll*(spr[j]-spr[i-1])*f(n/i,m/i); //spr为g的前缀和
i=j;
}
return res;
}
YY的GCD
给定
随着询问次数的增加,上一题的方法已经无法通过,考虑优化。
拆开
关键代码:
inline void init(int n) {
Euler(n); //欧拉筛质数和mu
for(int i=1;i<=pcnt;i++) { //枚举质数求h的值
for(int j=1;pr[i]*j<=n;j++) {
sum[pr[i]*j]+=mu[j];
}
}
for(int i=1;i<=n;i++) {
smu[i]=smu[i-1]+mu[i];
sum[i]+=sum[i-1];
}
}
inline long long calc(int n,int m) {
if(n>m) swap(n,m);
long long res=0;
for(int i=1;i<=n;i++) { //数论分块
int j=min(n/(n/i),m/(m/i));
res+=1ll*(sum[j]-sum[i-1])*(n/i)*(m/i);
i=j;
}
return res;
}
当然,
和之前考虑欧拉筛
-
当
为质数时,由于 不是质数,故只会枚举到 的情况,即当 为质数时, 。 -
当
且 时, 是 相比于 多出的一个新的质因子,这会导致 中枚举到的 变成 ,根据 的定义得出 ,则有 。再考虑由于 而多出来的部分,因为枚举到的 为质数,故只会多枚举一个 ,则只会多贡献一个 。 由此得出:当 且 时, 。 -
当
且 时, 是 的一个幂次大于等于 的质因子,根据 定义,由于枚举到的 均为质数,故当 时,所有的 。最后剩下一个新枚举到的 ,对结果的贡献为 。由此得出:当 且 时,
据此可以利用欧拉筛在
关键代码:
inline void Euler(int n) {
mu[1]=1;f[1]=0; //根据定义得到
for(int i=2;i<=n;i++) {
if(!vis[i]) mu[i]=-1,pr[++pcnt]=i,f[i]=1; //情况1
for(int j=1;j<=pcnt&&i*pr[j]<=n;j++) {
vis[i*pr[j]]=true;
if(i%pr[j]==0) {
mu[i*pr[j]]=0;
f[i*pr[j]]=mu[i];break; //情况2
}
mu[i*pr[j]]=-mu[i];
f[i*pr[j]]=-f[i]+mu[i]; //情况3
}
}
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+f[i];
}
inline long long calc(int n,int m) {
if(n>m) swap(n,m);
long long res=0;
for(int i=1;i<=n;i++) { //数论分块
int j=min(n/(n/i),m/(m/i));
res+=(sum[j]-sum[i-1])*(n/i)*(m/i);
i=j;
}
return res;
}
To be continued...
本文作者:Transparent
本文链接:https://www.cnblogs.com/xzmxzm/p/17207274.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步