莫比烏斯反演 講解

(同步個人博客 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

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