狄利克雷卷積&莫比烏斯反演總結
Prepare
1、\([P]\)表示當\(P\)為真時\([P]\)為\(1\),否則為\(0\)。
2、\(a|b\)指\(b\)被\(a\)整除。
3、一些奇怪常見的函數:
\(1(n)=1\)
\(id(n)=n\)
\(\sigma(n)=n的約數和\)
\(d(n)=n的約數個數\)
\(\epsilon(n)=[n==1]\)
狄利克雷卷積
數論函數
數論函數指一類定義域是正整數,值域是一個數集的函數。
加法:逐項相加就可以辣\((f+g)(x)=f(x)+g(x)\)
數乘:用一個常數乘\((xf)(n)=x*f(n)\)
狄利克雷卷積
定義兩個數論函數的狄利克雷卷積\(*\):
若\(t=f*g\)則
等價於
狄利克雷卷積有以下性質(兩個數論函數相等,是指兩個函數的每一項都相等):
1、交換律\(f*g=g*f\)
2、結合律\(f*(g*h)=(f*g)*h\)
3、分配律\(f*h+g*h=(f+g)*h\)
4、沒有名字\((xf)*g=x(f*g)\)
5、單位元\(\epsilon*f=f\),其中\(\epsilon(n)=[n==1]\)
6、逆元:對於每一個\(f(1)\neq 0\)的函數\(f\),都有\(f*g=\epsilon\)
討論一下第六個結論,如何求一個函數的逆呢?
只需要定義
這樣的話
積性函數
如果一個數論函數\(f\)有當\(gcd(n,m)==1\)時
就稱\(f\)為積性函數。
一些常見的積性函數:
\(\epsilon(n)=[n==1]\),\(id(n)=n\),\(id^{k}(n)=n^k\)
事實上他們也滿足完全積性(即當\(gcd(n,m)\neq1\)時,也有\(f(nm)=f(n)f(m)\))
特殊的,我們令\(id^0(n)=1(n)=1\)
還有兩個普通的積性函數
\(d(n)=n的約數和\)、\(\varphi(n)=[1,n]中與n互質的數的個數\)
還有兩個重要結論:
兩個積性函數的狄利克雷卷積是積性函數。
積性函數的逆是積性函數。
積性函數有什么用呢?
它可以線性篩
然而還有更有用的\(---\)
莫比烏斯反演
一些理論
我們定義\(1\)的逆是\(\mu\)
這樣的話,如果\(g=f*1\),就有\(f=f*1*\mu=g*\mu\)
換句話說,就是
也可以這樣子
例子
怎么用呢?舉幾個例子(以下情況默認\(n\leq m\))
\(Eg1\)
求
然后怎么辦呢?
設
則
考慮\(g(x)\)是什么
即
帶回\(f(1)\)
這個用整除分塊可以做到\(O(\sqrt n)\)
\(Eg2\)
求
可化為
設
則
套入我們剛才在\(Eg1\)求得的
化到現在是\(O(n)\)的,因為前后都可以數論分塊
但是我們能做得更好
令\(T=id\)
原式化為
乍一看還是\(O(n)\)的呀,但是對於后面那一坨
兩個積性函數相乘,可以線性篩呀!!
所以復雜度被我們壓到了\(O(\sqrt n)\)
\(upd\;on\;2019.3.9:\)
發現以前沒有講線性篩,導致現在自己都不知道是怎么搞得了。。。
線性篩
我們篩\(\mu\)的函數是長這樣的(自動認為有模數):
int prime[MAX_N], mu[MAX_N]
bool nprime[MAX_N];
void sieve() {
mu[1] = 1;
for (int i = 2; i <= N; i++) {
if (!nprime[i]) prime[++tot] = i, mu[i] = Mod - 1;
for (int j = 1; i * prime[j] <= N; j++) {
nprime[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
mu[i * prime[j]] = Mod - mu[i];
}
}
}
其實就是和篩素數的是一樣的,
if (i % prime[j] == 0) break;
這句話保證了復雜度,因為你存的素數是遞增的,
如果\(i\)被\(prime[j]\)整除后,\(i*prime[j+k](k>0)\)一定可以被\(prime[j]*x\)的形式表示出來。
那么就有我們下面的一個問題:
\(Eg3\)
給定你一個數組\(f\),求
其中\(n,m\leq 10^7\),數據組數\(T\leq 10^4\)。
由我們上面推的東西,將\(f\)看作一個數論函數,可以知道只要求出一個函數\(g=\mu * f\)的前綴和,
這個問題就解決了。
一下是解決這個問題的幾種方法(蒯的):
void get_g_1(int N, const int *f, int *g) {
for (int i = 1; i <= N; i++) g[i] = 0;
for (int i = 1; i <= N; i++)
for (int j = 1; i * j <= N; j++)
g[i * j] = (g[i * j] + mu[i] * f[j]) % mod;
} // 依照定義,O(nlogn)
void get_g_2(int N, const int *f, int *g) {
for (int i = 1; i <= N; i++) g[i] = f[i];
for (int i = 1; i <= N; i++)
for (int j = 2; i * j <= N; j++)
g[i * j] = (g[i * j] - g[i]) % mod;
} // 類似求狄利克雷卷積逆的方式,不需要線性篩 mu ,O(nlogn)
void get_g_3(int N, const int *f, int *g) {
for (int i = 1; i <= N; i++) g[i] = f[i];
for (int i = 0; i < prime_count; i++)
for (int j = N / prime[i]; j >= 1; j--)
g[j * prime[i]] = (g[j * prime[i]] - g[j]) % mod;
} // Magic! O(nloglogn)
對於最后一種方法,理解成dp:
那么轉移:
復雜度\(O(n\log\log n)\)。