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