【莫比乌斯反演】专题总结

为什么感觉我好像之前学过这玩意- -?

 

莫比乌斯反演

莫比乌斯反演就是一个能求gcd为多少的个数有几个的东西- -(反正我只知道这么用)

f(x)表示gcd为x的倍数的个数

g(x)表示gcd为x的个数

莫比乌斯反演的基本形式是

f(n) = Σ (g(d))   d | n
g(n) = Σ (g(d) * miu (n / d))  d | n

还有一种形式是

f(n) = Σ (g(d))  n | d
g(n) = Σ (f(d) * miu (d / n))  n | d

其中miu(i)是莫比乌斯函数

 

莫比乌斯函数

           1           (i==1)

miu[i]=-1^k      (i为k个不同质数相乘)

          0            (其他情况)

求莫比乌斯函数可以用线性筛法O(n)求出

 

O(n)求莫比乌斯函数代码

 1 void makepri(){
 2     miu[1]=1;
 3     for (int i=2;i<N;i++){
 4         if (!bo[i]) pri[++pri[0]]=i,miu[i]=-1;
 5         for (int j=1;j<=pri[0] && i*pri[j]<N;j++){
 6             bo[i*pri[j]]=1;
 7             if (i%pri[j]==0){
 8                 miu[i*pri[j]]=0;
 9                 break;
10             }else miu[i*pri[j]]=-miu[i];
11         }
12     }
13 }

 

优化

用暴力for求g[i]的时间复杂度是O(n)的

但是有的题目会给出很多询问 导致O(n)不能满足出题人- -

假设题目要求gcd(x,y)=1的个数(1<=x<=n,1<=y<=m)

那么f[i]=[n/i]*[m/i]

我们发现[n/i]的值只有√n种 可以按值分块求答案

代码还很短- -

for (int i=1,last=0;i<=n && i<=m;i=last+1){
    last=std::min(n/(n/i),m/(m/i));
    res+=(sum[last]-sum[i-1])*(n/i)*(m/i);
}

 

posted @ 2014-05-21 21:43  g_word  阅读(429)  评论(0编辑  收藏  举报