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


     

    莫比乌斯反演的应用范围

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

     

    积性函数

     

    定义:若 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]

     

  • 相关阅读:
    camp待补
    ZOJ
    ZOJ
    ZOJ
    CodeForces
    CodeForces
    POJ 3278 Catch That Cow(简单BFS)
    POJ 2251 Dungeon Master(三维BFS)
    POJ 1218 THE DRUNK JAILER(类开灯问题,完全平方数)
    HDU 2053 Switch Game(开灯问题,完全平方数)
  • 原文地址:https://www.cnblogs.com/FloraLOVERyuuji/p/10539217.html
Copyright © 2020-2023  润新知