莫比乌斯反演 讲解

(同步个人博客 http://sxysxy.org/blogs/10)

 懵逼乌斯反演?反正WC2016上我是听得一脸懵逼的,也就是学会了这个名词的认读与拼写。->, ->(雾)

 这个东西可以用来简化一些运算,对于定义在两个正整数集合上的两个函数f(x)与g(x),满足
,根据这个式子,我们可以发现

 当我们想要计算g(x)的时候,如果直接计算g(x)难以计算(太慢太慢太慢TLE TLE TLE 爆零爆零爆零退役退役退役…….),考虑用f(x)来计算g(x)。则从上面表格里面发现的规律,这样表示g(x)

其中的μ函数是我们觉得此处应该有的一个函数(也就是后面所说的莫比乌斯函数),根据上表发现的规律,他的值域为{0, 1, -1} (这是显然的,不清楚可以自行脑补)

 通常,通过换元法,令t = x/d ,则d = x/t,g(x) = Σ (下面d|x) f(d)*μ(x/d) = Σ (下面t|x) f(x/t)*μ(t)。也就是得到这样的写法:

那么最后得到的结论就是
这里写图片描述 具体详细证明安利一波百度百科 http://baike.baidu.com/link?url=gpiZ99LcvuK_Vf7K4KszIWRgmmRQkcIpyir9IgzBkyaA3FgVCAG7mouZNVzkZgqyYWdNYUpIwFOixrM6fc8fb_#3 以及WC2016的教师授课讲义

稍有常识的人都能看出,现在问题的关键就是这个μ函数

观察开头给出的表格,经过简单的代数变换,易发现μ函数的一些取值,即

μ(1) = 1

μ(2) = -1

μ(3) = -1

μ(4) = 0

μ(5) = -1

μ(6) = 1

定义莫比乌斯函数μ(d):

(1) d = 1时,μ(d) = 1

(2) d = p1 × p2 × … × pk ,(其中p1,p2..pk是互不相同的质数) 则μ(d) = (-1)^k (-1的k次幂)

(3) 其余情况 μ(d) = 0

莫比乌斯函数是积性函数

 这里的积性函数指的是数论中的积性函数,如果对于一个函数f(ab) = f(a)f(b),其中a与b互质,则f(x)就是个在数论上的积性函数。如果对于任意a,b都满足f(ab) = f(a)f(b),则称它为完全积性函数。

证明莫比乌斯函数是积性函数:

对于μ(a),μ(b),当a与b互质时,直接代入μ(ab)与μ(a)μ(b),就易证μ(ab) = μ(a)μ(b)

计算莫比乌斯函数

利用莫比乌斯函数是积性函数的性质,配合线性筛素数算法即可快速计算莫比乌斯函数。下面给出一个计算莫比乌斯函数的代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define MAXN 101
bool vis[MAXN];
int primes[MAXN];
int miu[MAXN];

int calc(int limit)
{
    memset(vis, false, sizeof(vis));
    memset(miu, 0, sizeof(miu));
    int tot = 0;
    miu[1] = 1;   //根据定义,μ(1) = 1
    for(int i = 2; i <= limit; i++)
    {
        if(!vis[i])  //未发现的质数
        {
            primes[tot++] = i;
            miu[i] = -1;
                //质数,除了1以外,只有它自己是自己的质因数
                //因此根据定义,μ(i) = (-1)^1 = -1
        }
        for(int j = 0; j < tot; j++)
        {
            int k = i*primes[j];   
            if(k > limit)break;
            vis[k] = true;
            if(i % primes[j]) //i不是primes[j]的整数倍时,i*primes[j]就不会包含相同质因子。
                miu[k] = -miu[i];     //此时根据其积性函数的特性得 miu[k] = miu[i]*miu[primes[j]],因为primes[j]是质数,miu值为-1
            else                      //然后化简得miu[k] = -miu[i]; (perfect!)
                break;                
        }
    }
}

int main()
{
    calc(100);
    for(int i = 1; i <= 100; i++)
        printf("μ(%d) = %d \n", i, miu[i]);
    return 0;
}

后来的补充:

在不少oi/acm题目中,常常可以通过分块莫比乌斯函数区间和(用前缀和实现)来加速。具体的一个例子可以看这里: http://sxysxy.org/blogs/11

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章