莫比乌斯反演

莫比乌斯反演在数论中占有重要的地位,许多情况下能大大简化运算。那么我们先来认识莫比乌斯反演公式。

 

定理:是定义在非负整数集合上的两个函数,并且满足条件,那么我们得到结论

 

     

 

在上面的公式中有一个函数,它的定义如下:

 

    (1)若,那么

    (2)若均为互异素数,那么

    (3)其它情况下

 

 

对于函数,它有如下的常见性质:

 

    (1)对任意正整数

  

                           

 

        (2)对任意正整数

 

        

 

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

[cpp] view plain copy
  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后还有漏计算了的,那么有哪些呢?

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

 

代码:

[cpp] view plain copy
  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\n",Solve(n));  
  63.     return 0;  
  64. }  


 

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

 

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

     围是

 

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

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

 

    

 

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

 

     

 

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

 

     对于本题,我们设

 

     为满足的对数

     为满足的对数

 

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

 

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

 

    

 

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

 

     我们设,那么继续得到

 

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

 

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

 

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

 

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

 

      

 

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

 

       

 

[cpp] view plain copy
  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;  


(2)

题意:给定N,M,求满足1<=i<=N,1<=j<=M,gcd(i,j)为质数的有序数对(i,j)的对数,N,M<=10^7 测试数据组数<=10^4

莫比乌斯反演好神奇...

一直只知道百度百科上说的那一种形式:F(n)=Σ(d|n)f(d)f(n)=Σ(d|n)miu(d)F(n/d)

查了题解才知道有另一种形式...也就是这道题会用到的形式...

F(x)=Σ(x|d)f(d)f(x)=Σ(x|d)miu(d/x)F(d) 其中d在某个限定范围内,接下来的问题就是:

对于给定的N,M,求1<=i<=N,1<=j<=M,gcd(i,j)=1的有序数对(i,j)的对数。再枚举质数p后,把N'=N/p,M'=M/p时这个问题的解累加起来就可以了。

对于给定的N,M,设f(x)1<=i<=N,1<=j<=M,gcd(i,j)=x(i,j)的对数,F(x)1<=i<=N,1<=j<=M,x|gcd(i,j)(i,j)的对数。

显然有F(x)=(N/x)*(M/x) (N/xM/x都是下取整)      F(x)=Σ(x|d)f(d)

反演得到
                 f(x)=Σ(x|d)miu(d/x)F(d)=Σ(x|d)miu(d/x)*(N/d)*(M/d)

这个问题的解是f(1),把x=1代入得到 :f(1)=Σ(d)miu(d)*(N/d)*(M/d)

做完了?当然没有。不TLE见鬼了。考察答案的表达式:Ans=Σ(质数p) [ Σ(d)miu(d)*(N/pd)*(M/pd) ]

换个角度,先枚举T=pd,再去枚举p,则d=T/p。得到Ans=∑(T) [ ∑(质数p|T)miu(T/p)*(N/T)*(M/T) ]

这么做的好处立刻就显现出来了,N/TM/Tp无关!

Ans=∑(T) [ (N/T)*(M/T) *∑(质数p|T)miu(T/p) ]

再令g(x)=sigma(质数p|x)miu(x/p) 改写上式为

Ans=∑(T)(N/T)*(M/T)*g(T) 

恩,非常和谐。

接下来全副精力对付g(x)。仍然考虑线性筛搞定他。

假设枚举到i,质数枚举到p(程序里的prime[j]),要更新A=i*p的信息。

1. p|i
    这时A的素数分解式中,p这一项的次数>=2

  考虑g(A)的求和式:

  如果枚举的质数p'不等于pA/p'就也会有p这一项,次数>=2,这时候miu(A/p')=0

    如果枚举的质数p'=pA/p=i,这一项就是miu(i)

    因此g(A)=miu(i)
2. p!|i (i%p!=0)

   这时候Ai多一个质因子p,对miu(i)分情况讨论。

  2.1 miu(i)==0 (i有大于1次的项)

           这时A除去任何一个p'都会留下i的那个大于1次的项,除非是下面这一种非常特殊的情况:

    2.1.1 i的素数分解式中,大于1次的项只有一个,且这一项为2次。记这一项为p0

         这时除去任何一个p'!=p0都会留下这一项,但是除去p0则会得到A/p0——这个数所有的项都是1次的。因此g(A)=miu(A/p0)

          2.1.2 i的素数分解式大于1次的项不止一个 或者 大于1次的项唯一,但次数高于2次。易见g(A)=0

     2.2 miu(i)!=0 (i全是1这个时候A的项也全是1次。设r(x)x的质因子个数。

     则可以得到g(A)=r(A)*(-1)^(r(A)-1)。因为除以任何一个p'miu(A/p')都是一样的。

     同理g(i)=r(i)*(-1)^(r(i)-1),且有r(A)=r(i)+1 利用r(A)=r(i)+1可以方便地得到:g(A)g(i)异号,且绝对值比g(i)1

     亦即g(A)=(g(i)>0)?-1:1 -g(i) 

          g(A)的维护讨论完了。

完了?没完,看情况2.1.1,我们有这么个遗留问题:

如果x的大于1次的项唯一,且这一项为2次,则令f(x)为这个项,否则f(x)=1

事实上f(x)=1包含3种情况:

1. 大于1的项不唯一

2. 大于1次的项唯一但大于2次。

3. 全为1

12利用现有的结果无法区分,但事实上不需要区分。3则可以用miu(x)判出来。

好,我们来对付f(x),仍然是线性筛,变量意义同g(x)的讨论。

1. p|i

 Ai把最小因子p的次数加1得到,显然这一项的次数>=2

1.1 f(i)!=1 

1.1.1 如果f(i)=p,那么Ap的次数就是3次了,f(A)=1

1.1.2 如果f(i)!=p,那么A中大于1次的项就不唯一了,仍有f(A)=1

因此f(i)!=1必然有f(A)=1

1.2 i全为1次 即f(i)=1miu(i)!=0 这时显然f(A)=p

1.3 i不全为1次 即f(i)=1miu(i)=0 这时显然f(A)=1

2. p!|i

Ai多一个1次的质因数p,那么应有f(A)=f(i)

f(A)的递推也讨论完了。

完了?虽然剩下的工作很简单但是也是必不可少的..

回去看求和的式子:Ans=∑(T)(N/T)*(M/T)*g(T)

直接做是O(min(M,N))的,别忘了有1W组数据啊。

但是我们有个结论:对于给定的N(N/T)的取值只有sqrt(N)个

那么给定的N,M(N/T)*(M/T)就只有sqrt(N)+sqrt(M)个了,而且相同的取值当然是连成一段的。

由此,分段来计算。令gs(x)g(x)的前缀和。

对于枚举到的i,我们希望找到最大的使得(N/i)*(M/i)=(N/j)*(M/j)j值。

这可能有点困难,我们退而求其次,求出最大的使得N/i=N/jM/i=M/jj值,效果不会变差太多。

这个就很简单了,可以得到j=min(N/(N/i),M/(M/i))于是把i~j这一段整体计算即可。

[cpp]  view plain copy
  1. #include <cstdio>  
  2. #include<algorithm>  
  3. #include<cstdlib>  
  4. #include<cstring>  
  5. const int maxp=10000001;  
  6. int pr[maxp],miu[maxp],g[maxp],f[maxp];  
  7. long long gs[maxp];  
  8. bool flag[maxp];  
  9. int gcd(int x,int y)  
  10. {  
  11.     int t;  
  12.     if(x<y)  
  13.     {  
  14.         t=x;  
  15.         x=y;  
  16.         y=t;  
  17.     }  
  18.     while(y)  
  19.     {  
  20.         t=x%y;  
  21.         x=y;  
  22.         y=t;  
  23.     }  
  24.     return x;  
  25. }  
  26.   
  27. void init()  
  28. {  
  29.     int i,j,A;  
  30.     miu[1]=1;  
  31.     for(i=2;i<maxp;++i)  
  32.     {  
  33.         if(!flag[i])  
  34.         {  
  35.             pr[++pr[0]]=i;  
  36.             miu[i]=-1;  
  37.             f[i]=g[i]=1;  
  38.         }  
  39.         for(j=1;j<=pr[0]&&i*pr[j]<maxp;++j)  
  40.         {  
  41.             flag[A=i*pr[j]]=true;  
  42.             if(i%pr[j]!=0)  
  43.             {  
  44.                 miu[A]=-miu[i];  
  45.                 f[A]=f[i];  
  46.                 if(miu[i]==0)  
  47.                      g[A]=(f[i]!=1)?miu[A/f[i]]:0;  
  48.                 else  
  49.                      g[A]=(g[i]>0)?(-g[i]-1):(-g[i]+1);  
  50.             }  
  51.             else  
  52.             {  
  53.                 miu[A]=0;  
  54.                 if(f[i]==1)  
  55.                      f[A]=(miu[i]==0)?1:pr[j];  
  56.                 else  
  57.                      f[A]=1;  
  58.                 g[A]=miu[i];  
  59.                 break;  
  60.             }  
  61.         }  
  62.     }  
  63.     for(i=2;i<maxp;++i)  
  64.          gs[i]=gs[i-1]+g[i];  
  65. }  
  66.   
  67. int main()  
  68. {  
  69.     init();  
  70.     int T,N,M,i,j,t,t1,t2;  
  71.     long long ans;  
  72.     scanf("%d",&T);  
  73.     while(T--)  
  74.     {  
  75.         scanf("%d%d",&N,&M);  
  76.         if(N<M){t=N;N=M;M=t;}  
  77.         ans=0;  
  78.         for(i=2;i<=M;i=t+1)  
  79.         {  
  80.             t1=N/(N/i);  
  81.             t2=M/(M/i);  
  82.             t=(t1<t2)?t1:t2;  
  83.             ans+=(gs[t]-gs[i-1])*(N/i)*(M/i);  
  84.         }  
  85.         printf("%lld\n",ans);  
  86.     }  
  87.     return 0;  
  88. }  

题目:http://acm.uestc.edu.cn/#/problem/show/811

 

题意:给定两个正整数,其中,求的值,其中

 

分析:本题是典型的莫比乌斯反演问题。那么,怎么反演呢?

     首先,我们枚举的所有值,根据以前学的莫比乌斯反演,可以很容易得到

 

     ,其中

 

     我们设

 

     ,那么得到,反演后得到

 

     所以就是

 

     可以看出枚举的所有因子递归下去就行。。。现在的关键问题是如何计算

 

     的表达式为

 

     由于不大,所以可以直接用自然数幂和,而在一段连续的区间值保持不变,思路跟下面这道题差不多。

 

     题目:http://blog.csdn.net/acdreamers/article/details/10249611

 

      也就是说的时间复杂度为。加上递归的部分,本题总的时间复杂度为

 

 

     代码:

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <string.h>  
  3. #include <stdio.h>  
  4. #include <math.h>  
  5.   
  6. using namespace std;  
  7. const int N = 50000005;  
  8. typedef long long LL;  
  9. const LL MOD = 1000000007;  
  10.   
  11. LL dp[N];  
  12.   
  13. LL mu(LL n,int k)  
  14. {  
  15.     LL ans = 1;  
  16.     for(int i=0; i<k; i++)  
  17.     {  
  18.         ans *= n;  
  19.         ans %= MOD;  
  20.     }  
  21.     return ans;  
  22. }  
  23.   
  24. LL calc(LL n,int k)  
  25. {  
  26.     if(k == 1) return ((n%MOD)*((n+1)%MOD))%MOD*500000004%MOD;  
  27.     if(k == 2)  
  28.     {  
  29.         LL a = n % MOD;  
  30.         LL b = (n+1) % MOD;  
  31.         LL c = (2*n+1) % MOD;  
  32.         return a*b%MOD*c%MOD*166666668%MOD;  
  33.     }  
  34.     if(k == 3)  
  35.     {  
  36.         LL t = ((n%MOD)*((n+1)%MOD))%MOD*500000004%MOD;  
  37.         return t * t % MOD;  
  38.     }  
  39.     if(k == 4)  
  40.     {  
  41.         LL t = 6*mu(n,5)%MOD + 15*mu(n,4)%MOD + 10*mu(n,3)%MOD -n%MOD;  
  42.         t %= MOD;  
  43.         t += MOD;  
  44.         t %= MOD;  
  45.         t *= 233333335;  
  46.         t %= MOD;  
  47.         return t;  
  48.     }  
  49.     if(k == 5)  
  50.     {  
  51.         LL t = 2*mu(n,6)%MOD + 6*mu(n,5)%MOD + 5*mu(n,4)%MOD -mu(n,2)%MOD;  
  52.         t %= MOD;  
  53.         t += MOD;  
  54.         t %= MOD;  
  55.         t *= 83333334;  
  56.         t %= MOD;  
  57.         return t;  
  58.     }  
  59. }  
  60.   
  61. LL sum(LL n,int k)  
  62. {  
  63.     LL ans = 0;  
  64.     LL T = (LL)sqrt(1.0*n);  
  65.     for(int i=1; i<=T; i++)  
  66.     {  
  67.         LL t = (n/i) % MOD;  
  68.         LL a = t * t % MOD;  
  69.         LL b = mu(i,k) * a % MOD;  
  70.         LL c = i * i % MOD;  
  71.         LL L = n/(i+1) + 1;  
  72.         LL R = n/i;  
  73.         LL d = calc(R,k) - calc(L-1,k);  
  74.         d %= MOD;  
  75.         d += MOD;  
  76.         d %= MOD;  
  77.         c = c * d % MOD;  
  78.         ans += b + c;  
  79.         ans %= MOD;  
  80.     }  
  81.     if(T*T == n)  
  82.         ans -= mu(T,k+2);  
  83.     ans %= MOD;  
  84.     ans += MOD;  
  85.     ans %= MOD;  
  86.     return ans;  
  87. }  
  88.   
  89. LL dfs(LL n,int k)  
  90. {  
  91.     if(n < N && dp[n]) return dp[n];  
  92.     if(n == 1) return 1;  
  93.     LL ans = sum(n,k);  
  94.     LL tmp = 0;  
  95.     for(LL i=1; i*i<=n; i++)  
  96.     {  
  97.         if(i*i == n)  
  98.         {  
  99.             tmp %= MOD;  
  100.             tmp += dfs(i,k);  
  101.             tmp %= MOD;  
  102.         }  
  103.         else  
  104.         {  
  105.             tmp %= MOD;  
  106.             tmp += dfs(i,k);  
  107.             tmp %= MOD;  
  108.             if(i == 1) continue;  
  109.             tmp += dfs(n/i,k);  
  110.             tmp %= MOD;  
  111.         }  
  112.     }  
  113.     if(n < N)  
  114.         dp[n] = ((ans-tmp)%MOD + MOD) % MOD;  
  115.     return ((ans-tmp)%MOD + MOD) % MOD;  
  116. }  
  117.   
  118. int main()  
  119. {  
  120.     LL n,k;  
  121.     memset(dp,0,sizeof(dp));  
  122.     scanf("%lld%lld",&n,&k);  
  123.     printf("%lld\n",dfs(n,k));  
  124.     return 0;  


你可能感兴趣的:(莫比乌斯反演)