莫比烏斯反演的前置知識
定義
設\(f,g\)是數論函數,考慮數論函數\(h\)滿足
\[h(n)=\sum_{d|n}f(d)g(\frac{n}{d}) \]
則稱\(h\)為\(f\)和\(g\)的狄利克雷卷積,記作\(h=f*g\),這里的\(*\)表示卷積。
比如\(h(6)=f(1)*g(6)+f(2)*g(3)+f(3)*g(2)+f(6)*g(1)\)
性質
- 單位函數\(\epsilon\)是狄利克雷卷積的單位元,即對於任意函數\(f\),有\(\epsilon*f=f*\epsilon=f\)。
- 狄利克雷卷積滿足交換律和結合律。
- 如果\(f,g\)都是積性函數,那么\(f*g\)也是積性函數。
許多關系都可以用狄利克雷卷積來表示。
下面用\(1\)來表示取值恆為\(1\)的常函數,定義冪函數\(\text{Id}_{k}(n)=n^k,\text{Id=Id}_1\)。
除數函數的定義可以寫為:
\[\sigma_k=1*\text{Id}_k \]
歐拉函數的性質可以寫為:
\[\text{Id}=\varphi*1 \]
計算狄利克雷卷積
設\(f,g\)是數論函數,計算\(f\)和\(g\)的狄利克雷卷積在\(n\)處的值需要枚舉\(n\)的所有約數。
如果要計算\(f\)和\(g\)的狄利克雷卷積的前\(n\)項,可以枚舉\(1\)到\(n\)中每個數的倍數,根據調和數的相關結論,這樣做的復雜度是\(O(n\log n)\)。
求函數的逆
狄利克雷卷積有一個性質:對每個\(f(1)\neq0\)的函數\(f\),都存在一個函數\(g\)使得 \(f\ast g=\epsilon\)
那么我們如何求出一個函數的逆呢?
只需要定義:
\[g(n)=\frac 1{ f(1)}\left([n=1]-\sum\limits_{i\mid n, i\neq1} f(i) g\left(\frac ni\right)\right) \]
這樣的話
\[\begin{aligned}&\quad\sum_{i\mid n} f(i) g\left(\frac ni\right)\\&= f(1) g(n)+\sum_{i\mid n,i\neq1} f(i) g\left(\frac ni\right)\\&=[n=1]\end{aligned} \]
最后一步直接把\(g(n)\)的定義帶進去就好
即
\[= f(1)*\frac{1}{ f(1)}([n = 1] - \sum\limits_{i|n,i\neq1} f(i) g(\frac n i))+ \sum\limits_{i|n,i\neq1} f(i) g(\frac ni) \]
例題
P2303 [SDOI2012]Longge的問題
給定正整數\(n\),求
\[\sum_{i=1}^{n}gcd(i,n),n\leq2^{32} \]
枚舉\(\text{gcd}\):
\[\begin{align*} \sum_{i=1}^{n}gcd(i,n) &= \sum_{d|n}d\sum_{i=1}^{n}[gcd(i,n)=d] \\ &= \sum_{d|n}d\sum_{i=1}^{\frac{n}{d}}[gcd(i,\frac{n}{d})=1] \\ &= \sum_{d|n}d\varphi(\frac{n}{d}) \end{align*}\]
枚舉\(n\)的約數直接求。答案是積性的。
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#define int long long
using namespace std;
inline int read() {
char c = getchar(); int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
int n, ans;
int euler(int x) {
int ans = x, rt = sqrt(x);
for (int i = 2; i <= rt; i++) {
if (x % i == 0) {
ans = ans - ans / i;
while (x % i == 0) x /= i;
}
}
if (x > 1) ans = ans - ans / x;
return ans;
}
signed main() {
n = read();
int x = sqrt(n);
for (int i = 1; i <= x; i++) {
if (n % i == 0) {
ans += euler(n / i) * i;
if (i * i != n) ans += euler(i) * (n / i);
}
}
cout << ans << '\n';
return 0;
}