【暖*墟】#数论# 莫比乌斯反演的学习与练习

 

莫比乌斯反演的应用范围

 

一些函数很难直接求值,而容易求出其倍数和或约数和,那么可以通过莫比乌斯反演求得原函数的值。

 

积性函数

 

定义:若 gcd(x,y)=1 ,且 f(xy)=f(x)f(y),则 f(n) 为积性函数。

性质:若 f(x) 和 g(x) 均为积性函数,则以下函数也为积性函数。

 

常见积性函数

 

Dirichlet 卷积

 

 

  • Dirichlet 卷积满足交换律、结合律、分配律。


其中 ε 为 Dirichlet 卷积的单位元(任何函数卷 ε 都为其本身)。

 

 

莫比乌斯函数

 

 

 

其中最重要的性质就是

 

void get_mu(int n){

mu[
1]=1; for(int i=2;i<=n;i++){
if(!vis[i]) primes[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&primes[j]*i<=n;j++){
vis[primes[j]
*i]=1;
if(i%primes[j]==0) break;
else mu[i*primes[j]]=-mu[i]; } } }

 

莫比乌斯反演

 

 

  • 公式可以进一步转化为:

 

相关习题练习

 

T1:【p3455】ZAP / 【p4450】双亲数

#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

//【p3455】ZAP
// 求∑(i=1~n)∑(j=1~m)[gcd(x,y)=d]

// 设f(d)为 gcd(i,j)=d 的个数,F(n)为 gcd(i,j)=d和d的倍数 的个数。
// 即:f(d)=∑(i=1~n)∑(j=1~m)[gcd(i,j)=d],F(n)=∑(n|d)f(d)=⌊N/n⌋⌊M/n⌋。
// 则可以得到:f(n)=∑(n|d) μ(⌊d/n⌋)*F(d)。

// 接下来的推导公式见:http://www.cnblogs.com/peng-ym/p/8652288.html

const int N=10000019;

bool vis[N]; int primes[N],cnt=0,mu[N],g[N]; ll sum[N];

void get_mu(int n){
    mu[1]=1; for(int i=2;i<=n;i++){
        if(!vis[i]) primes[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&primes[j]*i<=n;j++){
            vis[primes[j]*i]=1;
            if(i%primes[j]==0) break;
            else mu[i*primes[j]]=-mu[i];
        }
    } for(int i=1;i<=n;i++) sum[i]=sum[i-1]+mu[i];
}

int main(){
    int T,n,m,d; cin>>T; get_mu(50000);
    while(T--){
        scanf("%d%d%d",&n,&m,&d); n=n/d,m=m/d;
        ll ans=0; for(int l=1,r;l<=min(n,m);l=r+1){
            r=min(n/(n/l),m/(m/l)); //整除分块
            ans+=1LL*(n/l)*(m/l)*(sum[r]-sum[l-1]);
        } cout<<ans<<endl; //满足gcd(x,y)=d的(x,y)对数
    }
}
【p3455】ZAP // 求 ∑(i=1~n)∑(j=1~m)[gcd(x,y)=d]

 

T2:【p2522】problem B

  • ZAP+简单容斥原理(类似于二维前缀和的容斥):

ans=Ans((1,b),(1,d))−Ans((1,b),(1,c−1))−Ans((1,a−1),(1,d))+Ans((1,a−1),(1,c−1))

#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

//【p2522】problem B //'ZAP'的一般情况:求∑(i=a~b)∑(j=c~d)[gcd(x,y)=k]

// 设f(d)为 gcd(i,j)=d 的个数,F(n)为 gcd(i,j)=d和d的倍数 的个数。
// 即:f(d)=∑(i=1~n)∑(j=1~m)[gcd(i,j)=d],F(n)=∑(n|d)f(d)=⌊N/n⌋⌊M/n⌋。
// 则可以得到:f(n)=∑(n|d) μ(⌊d/n⌋)*F(d)。

// ZAP+简单容斥原理即可(类似于二维前缀和的容斥):
// ans=Ans((1,b),(1,d))−Ans((1,b),(1,c−1))−Ans((1,a−1),(1,d))+Ans((1,a−1),(1,c−1))

const int N=10000019;

bool vis[N]; int primes[N],cnt=0,mu[N],k; ll sum[N];

void get_mu(int n){
    mu[1]=1; for(int i=2;i<=n;i++){
        if(!vis[i]) primes[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&primes[j]*i<=n;j++){
            vis[primes[j]*i]=1;
            if(i%primes[j]==0) break;
            else mu[i*primes[j]]=-mu[i];
        }
    } for(int i=1;i<=n;i++) sum[i]=sum[i-1]+mu[i];
}

ll calc(int n,int m){ n=n/k,m=m/k;
    ll ans=0; for(int l=1,r;l<=min(n,m);l=r+1){
        r=min(n/(n/l),m/(m/l)); //整除分块
        ans+=1LL*(n/l)*(m/l)*(sum[r]-sum[l-1]);
    } return ans; //1~n,1~m,满足gcd(x,y)=k的(x,y)对数
}

int main(){ int T,a,b,c,d; cin>>T; get_mu(50000);
  while(T--){ scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
    cout<<calc(b,d)-calc(b,c-1)-calc(a-1,d)+calc(a-1,c-1)<<endl; } }
【p2522】problem B //'ZAP'的一般情况:求∑(i=a~b)∑(j=c~d)[gcd(x,y)=k]

 

T3:【p2257】YY的GCD

  • 给定N, M,求1<=x<=N, 1<=y<=M且gcd(x,y)为质数的(x,y)有多少对。
#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

//【p2257】YY的GCD
// 给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对。

// ∑(i=1~n)∑(j=1~m)[gcd(x,y)=primes]

// 设f(d)为 gcd(i,j)=d 的个数,F(n)为 gcd(i,j)=d和d的倍数 的个数。
// 即:f(d)=∑(i=1~n)∑(j=1~m)[gcd(i,j)=d],F(n)=∑(n|d)f(d)=⌊N/n⌋⌊M/n⌋。
// 则可以得到:f(n)=∑(n|d) μ(⌊d/n⌋)*F(d)。

// 接下来的推导公式见:http://www.cnblogs.com/peng-ym/p/8652288.html

const int N=10000019;

bool vis[N]; int primes[N],cnt=0,mu[N],g[N]; ll sum[N];

void get_mu(int n){
    mu[1]=1; for(int i=2;i<=n;i++){
        if(!vis[i]) primes[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&primes[j]*i<=n;j++){
            vis[primes[j]*i]=1;
            if(i%primes[j]==0) break;
            else mu[i*primes[j]]=-mu[i];
        }
    } for(int j=1;j<=cnt;j++)
        for(int i=1;i*primes[j]<=n;i++) g[i*primes[j]]+=mu[i];
    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+(ll)g[i];
}

int main(){
    int T,n,m; cin>>T; get_mu(10000000);
    while(T--){
        scanf("%d%d",&n,&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]);
        } cout<<ans<<endl; //满足gcd(x,y)为质数的(x,y)对数
    }
}
【p2257】YY的GCD // 求 ∑(i=1~n)∑(j=1~m)[gcd(x,y)=primes] 

 

T4:【p3327】约数个数和

#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

//【p3327】约数个数和 // 设d(x)为x的约数个数,求 ∑(i=1~n)∑(j=1~m)d(i*j)

// 约数个数的公式:d(i*j)=∑(x|i)∑(y|j)[gcd(x,y)=1]。

// 设f(d)为 gcd(i,j)=d 的个数,F(n)为 gcd(i,j)=d和d的倍数 的个数。
// 即:f(d)=∑(i=1~n)∑(j=1~m)[gcd(i,j)=d],F(n)=∑(n|d)f(d)=⌊N/n⌋⌊M/n⌋。
// 则可以得到:f(n)=∑(n|d) μ(⌊d/n⌋)*F(d)。

// Ans=∑(i=1~n)∑(j=1~m)∑(x|i)∑(y|j)[gcd(x,y)=1]。
// 根据公式推出:Ans=∑(i=1~n)∑(j=1~m)∑(x|i)∑(y|j)∑(d|gcd(x,y))μ(d)。
// 后续推导见:https://www.cnblogs.com/peng-ym/p/8667321.html

const int N=10000019;

bool vis[N]; int primes[N],cnt=0,mu[N],g[N]; ll sum[N];

void get_mu(int n){
    mu[1]=1; for(int i=2;i<=n;i++){
        if(!vis[i]) primes[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&primes[j]*i<=n;j++)
         {  vis[primes[j]*i]=1;
            if(i%primes[j]==0) break;
            else mu[i*primes[j]]=-mu[i];  }
    } for(int i=1;i<=n;i++) sum[i]=sum[i-1]+mu[i];
      for(int i=1;i<=n;i++){ 
        ll anss=0; for(int l=1,r;l<=i;l=r+1)
           r=(i/(i/l)),anss+=1LL*(r-l+1)*(i/l); g[i]=anss;
    }
}

int main(){
    int T,n,m; cin>>T; get_mu(50000);
    while(T--){ scanf("%d%d",&n,&m);
        ll ans=0; for(int l=1,r;l<=min(n,m);l=r+1){
            r=min(n/(n/l),m/(m/l)); //整除分块
            ans+=1LL*g[n/l]*1LL*g[m/l]*(sum[r]-sum[l-1]);
        } cout<<ans<<endl; //满足gcd(x,y)=d的(x,y)对数
    }
}
【p3327】约数个数和 // 设d(x)为x的约数个数,求 ∑(i=1~n)∑(j=1~m)d(i*j)。
  • 约数个数的公式:d(i*j)=∑(x|i)∑(y|j)[gcd(x,y)=1]

 

posted @ 2019-03-15 20:03  花神&缘浅flora  阅读(274)  评论(0编辑  收藏  举报