• 莫比乌斯反演(转)


    http://www.cnblogs.com/AOQNRMGYXLMV/p/4065628.html

    一、莫比乌斯(Möbius)函数

      对于每个正整数n(n ≥ 2),设它的质因数分解式为:

      

      根据这个式子定义n的莫比乌斯函数为:

      

      也就是如果n有平方因子,则为0. 否则是-1的质因数个数次方。

      举个简单的例子:6 = 2 × 3,所以;  9 = 3×3, 所以

      【命题一】

      对于正整数n有:

      

      也就是n>2时,所有n的约数对应函数值之和为0.

      

      证明:

      n=1的时候是显然的。

      n≥2时:

      ① 如果d中也含有平方因子,则其值为零。

      ② 设 , 若d中不含平方因子,则必有.

      所以有:

      得证。

    二、欧拉函数

      欧拉函数φ(n)定义为,1~n中与n的最大公约数为1的数字的个数。例如 φ(5) = 4, φ(6) = 2

      若p为质数,显然 φ(p) = p-1

      若n=pk, 则n的大于1的约数有p, 2p, 3p,...(pk-1-2)p, (pk-1-1)p共pk-1个数。所以φ(n) = pk-pk-1

      而且欧拉函数为积性函数(证明较为麻烦,略去),即若m、n互质,有φ(m)φ(n) = φ(mn)

      所以对于任意

      

      或者写成这种形式:

      

      

      莫比乌斯函数和欧拉函数的关系:

      

      这个不是太难证明,自己在纸上演算一下就明白了。

    三、莫比乌斯反演

      若定义在正整数集上的两个函数,f(n)和g(n)满足对任意n有:

            (1)

      

      则可以通过f来表示g:

          (2)

      反之,亦可以由关系(2)得到(1)

      

      证明:

      由式(1)有:

      

      于是:

      

      对于确定的d',d将取遍所有的因子,所以我们可以改变求和顺序:

      

      由上面的推导可知:只有当即n = d'时,等式右边才不为0。所以右边和式只剩下g(n)一项了。

      

      简单运用:

      上面说到莫比乌斯函数和欧拉函数的关系,

      变形为:

      视f(n) = n,  g(n) = φ(n), 上式相当于反演公式中的(2)式

      根据反演公式,可得到(1)式:

      

    线性筛选求莫比乌斯反演函数代码。

    1. void Init()  
    2. {  
    3.     memset(vis,0,sizeof(vis));  
    4.     mu[1] = 1;  
    5.     cnt = 0;  
    6.     for(int i=2; i<N; i++)  
    7.     {  
    8.         if(!vis[i])  
    9.         {  
    10.             prime[cnt++] = i;  
    11.             mu[i] = -1;  
    12.         }  
    13.         for(int j=0; j<cnt&&i*prime[j]<N; j++)  
    14.         {  
    15.             vis[i*prime[j]] = 1;  
    16.             if(i%prime[j]) mu[i*prime[j]] = -mu[i];  
    17.             else  
    18.             {  
    19.                 mu[i*prime[j]] = 0;  
    20.                 break;  
    21.             }  
    22.         }  
    23.     }  
    24. }  

    有了上面的知识,现在我们来证明莫比乌斯反演定理。

     

    证明

    证明完毕!

    嗯,有了莫比乌斯反演,很多问题都可以简化了,接下来我们来看看莫比乌斯反演在数论中如何简化运算的。

     

     

    题目:http://bz.cdqzoi.com/JudgeOnline/problem.php?id=2818

    题意:给一个正整数,其中,求使得为质数的的个数,

    分析:对于本题,因为是使得为质数,所以必然要枚举小于等于的质数,那么对于每一个质数,只

         需要求在区间中,满足有序对互质的对数。

     

         也就是说,现在问题转化为:在区间中,存在多少个有序对使得互质,这个问题就简单啦,因为

         是有序对,不妨设,那么我们如果枚举每一个,小于有多少个互素,这正是欧拉函数。所以

         我们可以递推法求欧拉函数,将得到的答案乘以2即可,但是这里乘以2后还有漏计算了的,那么有哪些呢?

         是且为素数的情况,再加上就行了。

     

    代码:

    1. #include <iostream>  
    2. #include <string.h>  
    3. #include <stdio.h>  
    4. #include <bitset>  
    5.   
    6. using namespace std;  
    7. typedef long long LL;  
    8. const int N = 10000010;  
    9.   
    10. bitset<N> prime;  
    11. LL phi[N];  
    12. LL f[N];  
    13. int p[N];  
    14. int k;  
    15.   
    16. void isprime()  
    17. {  
    18.     k = 0;  
    19.     prime.set();  
    20.     for(int i=2; i<N; i++)  
    21.     {  
    22.         if(prime[i])  
    23.         {  
    24.             p[k++] = i;  
    25.             for(int j=i+i; j<N; j+=i)  
    26.                 prime[j] = false;  
    27.         }  
    28.     }  
    29. }  
    30.   
    31. void Init()  
    32. {  
    33.     for(int i=1; i<N; i++)  phi[i] = i;  
    34.     for(int i=2; i<N; i+=2) phi[i] >>= 1;  
    35.     for(int i=3; i<N; i+=2)  
    36.     {  
    37.         if(phi[i] == i)  
    38.         {  
    39.             for(int j=i; j<N; j+=i)  
    40.                 phi[j] = phi[j] - phi[j] / i;  
    41.         }  
    42.     }  
    43.     f[1] = 0;  
    44.     for(int i=2;i<N;i++)  
    45.         f[i] = f[i-1] + (phi[i]<<1);  
    46. }  
    47.   
    48. LL Solve(int n)  
    49. {  
    50.     LL ans = 0;  
    51.     for(int i=0; i<k&&p[i]<=n; i++)  
    52.         ans += 1 + f[n/p[i]];  
    53.     return ans;  
    54. }  
    55.   
    56. int main()  
    57. {  
    58.     Init();  
    59.     isprime();  
    60.     int n;  
    61.     scanf("%d",&n);  
    62.     printf("%I64d ",Solve(n));  
    63.     return 0;  
    64. }  

    嗯,上题不算太难,普通的欧拉函数就可以搞定,接下来我们来看看它的升级版。

     

    题意:给定两个数,其中,求为质数的有多少对?其中的范

         围是

     

    分析:本题与上题不同的是不一定相同。在这里我们用莫比乌斯反演来解决,文章开头也说了它能大大简化

         运算。我们知道莫比乌斯反演的一般描述为:

     

         

     

         其实它还有另一种描述,本题也是用到这种。那就是:

     

         

     

         好了,到了这里,我们开始进入正题。。。

     

         对于本题,我们设

     

         为满足的对数

         为满足的对数

     

         那么,很显然,反演后得到

     

         因为题目要求是为质数,那么我们枚举每一个质数,然后得到

     

         

     

         如果直接这样做肯定TLE,那么我们必须优化。

     

         我们设,那么继续得到

     

         到了这里,可以看出如果我们可以先预处理出所有的对应的的值,那么本题就解决了。

     

         我们设,注意这里为素数,

     

         那么,我们枚举每一个,得到,现在分情况讨论:

     

         (1)如果整除,那么得到

     

           

     

         (2)如果不整除,那么得到

     

           

     

    1. #include <iostream>  
    2. #include <string.h>  
    3. #include <stdio.h>  
    4.   
    5. using namespace std;  
    6. typedef long long LL;  
    7. const int N = 10000005;  
    8.   
    9. bool vis[N];  
    10. int p[N];  
    11. int cnt;  
    12. int g[N],u[N],sum[N];  
    13.   
    14. void Init()  
    15. {  
    16.     memset(vis,0,sizeof(vis));  
    17.     u[1] = 1;  
    18.     cnt = 0;  
    19.     for(int i=2;i<N;i++)  
    20.     {  
    21.         if(!vis[i])  
    22.         {  
    23.             p[cnt++] = i;  
    24.             u[i] = -1;  
    25.             g[i] = 1;  
    26.         }  
    27.         for(int j=0;j<cnt&&i*p[j]<N;j++)  
    28.         {  
    29.             vis[i*p[j]] = 1;  
    30.             if(i%p[j])  
    31.             {  
    32.                 u[i*p[j]] = -u[i];  
    33.                 g[i*p[j]] = u[i] - g[i];  
    34.             }  
    35.             else  
    36.             {  
    37.                 u[i*p[j]] = 0;  
    38.                 g[i*p[j]] = u[i];  
    39.                 break;  
    40.             }  
    41.         }  
    42.     }  
    43.     sum[0] = 0;  
    44.     for(int i=1;i<N;i++)  
    45.         sum[i] = sum[i-1] + g[i];  
    46. }  
    47.   
    48. int main()  
    49. {  
    50.     Init();  
    51.     int T;  
    52.     scanf("%d",&T);  
    53.     while(T--)  
    54.     {  
    55.         LL n,m;  
    56.         cin>>n>>m;  
    57.         if(n > m) swap(n,m);  
    58.         LL ans = 0;  
    59.         for(int i=1,last;i<=n;i=last+1)  
    60.         {  
    61.             last = min(n/(n/i),m/(m/i));  
    62.             ans += (n/i)*(m/i)*(sum[last]-sum[i-1]);  
    63.         }  
    64.         cout<<ans<<endl;  
    65.     }  
    66.     return 0;  
    67. }  
  • 相关阅读:
    Java基础-String、StringBuffer、StringBuilder的区别
    Java基础-String类能被继承吗,为什么
    Java基础-Java中的基本数据类型是什么,各自占用多少字节
    JAVA关键字
    Java 栈(stack)与堆(heap) 详解
    Hibernate--hibernate.hbm.xml详解
    Hhibernate--映射继承关系
    C--c语言模拟java的linkedList
    C--调试基本命令
    C--指针
  • 原文地址:https://www.cnblogs.com/handsomecui/p/4995231.html
Copyright © 2020-2023  润新知