【目錄】
- 數論函數
- 積性函數
- 線性篩
- 狄利克雷卷積
- 杜教篩
- min_25篩
數論函數
定義
一個定義在正整數集上的實或復值函數$f(n)$叫作一個數論函數。函數
舉例
- 數列${a_n}$
- 階乘$n!$
- 冪函數$n^a$
積性函數
定義
若數論函數$f$不恆等於$0$,且當$\gcd(m,n)=1$時,有$f(mn)=f(m)f(n)$,則$f$叫作積性函數。若一個積性函數對於$\forall m,n$均有$f(mn)=f(m)f(n)$,則叫作徹底積性函數。優化
性質
- 若$f$爲積性函數,則$f(1)=1$ 證實: 設$\forall n\in\mathbb{N+}$,有$\gcd(n,1)=1$,故$f(n)=f(n)f(1)$,而$f(n)\neq0$,故$f(1)=1$
- 若$f$爲積性函數,且$n$的惟一分解式爲$n=p_1^{c_1}p_2^{c_2}\cdots p_k^{c_k}$,則$f(n)=f(p_1^{c_1})f(p_2^{c_2})\cdots f(p_k^{c_k})$ 這個就不證實了,很顯然的。。。
- 若$f,g$爲積性函數,則其狄利克雷卷積$f*g$爲積性函數。 這個下面會有證實的。
常見的積性函數
- $\varphi(n)$:歐拉函數
- $\mu(n)$:莫比烏斯函數
- $\gcd(n,k)(k爲定值時)$
- $d(n)=\sum\limits_{d|n}1$:約數個數
- $\sigma(n)=\sum\limits_{d|n}d$:約數和
- $\sigma_k(n)=\sum\limits_{d|n}d^k$:約數$k$次冪和
常見的徹底積性函數
- $I(n)=1$:常函數
- $id(n)=n$:單位函數
- $id_k(n)=n^k$:冪函數
- $\epsilon(n)=[n=1]$:元函數
線性篩
篩素數
最初,線性篩只是用來篩質數罷了。。。spa
void sieve(int n) { static int v[N], p[N], pr; // v[i] 表示 i 的最小質因子 // p[N] 和 pr 用來存質數表 for (int i = 2; i <= n; ++i) { if (v[i] == 0) v[i] = i, p[++pr] = i; // 沒被篩,是質數 for (int j = 1; j <= pr && i*p[j] <= n; ++j) { v[i*p[j]] = p[j]; if (i % p[j] == 0) break; // 當 i 是 p[j] 的倍數時 break } } }
代碼簡短,便於記憶。。。 然而背代碼是不可能的,這輩子不可能背代碼的。。。code
正確性證實
- 在篩的過程當中,上面的程序將$i \cdot p[j]$的最小質因子定爲$p[j]$。
- 先不考慮其正確性,將$i$惟一分解,獲得$i=p_1^{c_1}\cdots p_k^{c_k}$。
- 而此時必定有$p[j]\leq p_1$,由於當$p[j]=p_1$時就已經break了。
- 一樣因爲$p[j]\leq p_1$,$i \cdot p[j]$的最小質因子顯然爲$p[j]$。
線性時間複雜度證實
- 考慮每一個數$x$被篩到的緣由。
- 若$x$爲質數,則$x$被篩是理所固然的。
- 若$x$非質數,則$x$只會被其最小質因子篩到。
- 綜上述,每一個數會且僅會被篩一次,故時間複雜度爲$O(n)$。
篩積性函數
首先要徹底理解線性篩質數的過程。 對於積性函數$f(n)$,首先能夠判定$f(1)=1$; 由於對於$\forall n \in \mathbb{N_+}$,顯然有$\gcd(n,1)=1$,因此$f(n)=f(n)f(1)$,故$f(1)=1$。 注:如下用$\mathbb{P}$表示質數集 若想線性篩出積性函數$f(n)$,就必須能快速求出下面兩個值遞歸
- $f(p)(p\in\mathbb{P})$
- $f(p^k)(p\in\mathbb{P},k\in\mathbb{N_+})$
從新思考線性篩的過程it
- 將$i$惟一分解,獲得$i=p_1^{c_1}\cdots p_k^{c_k}$。
- 而此時必定有$p[j]\leq p_1$,由於當$p[j]=p_1$時就已經break了。
- 一樣因爲$p[j]\leq p_1$,$i \cdot p[j]$的最小質因子顯然爲$p[j]$。
考慮將其推廣到求積性函數class
- 若$p[j]<p_1$,則$\gcd(i,p[j])=1$,因而 $$f(i\cdot p[j])=f(i)f(p[j])$$
- 若$p[i]=p_1$,則須要記錄一個$low_i=p_1^{c_1}$,即$i$惟一分解式中最小質因子的冪。 能夠發現$\dfrac{i}{low_i}$的最小質因子必定大於$p_1$,而$p[j]=p_1$,故 $$\gcd\left(\dfrac{i}{low_i},low_i\cdot p[j]\right)=1$$ 因此 $$f(i\cdot p[j])=f\left(\dfrac{i}{low_i}\right)f(low_i\cdot p[j])$$
- 注意當$low_i=i$,即$i=p^k(p\in\mathbb{P},k\in\mathbb{N_+})$時,須要根據定義直接計算$f(i\cdot p[j])$
代碼以下map
void sieve(int n) { static int v[N], p[N], low[N], pr; f[1] = 1; for (int i = 2; i <= n; ++i) { if (!v[i]) { // i 爲質數 v[i] = i; // 最小質因子 low[i] = i; // 惟一分解式中最小質因子的冪 p[++pr] = i; // 加入質數表 f[i] = ...; // 由定義直接計算 } for (int j = 1; j <= pr && i*p[j] <= n; ++j) { v[i*p[j]] = p[j]; if (i % p[j] == 0) { low[i*p[j]] = low[i] * p[j]; // 遞推計算 low if (low[i] == i) f[i*p[j]] = ...; // 由定義直接計算 else f[i*p[j]] = f[i/low[i]] * f[low[i]*p[j]]; break; } else { low[i*p[j]] = p[j]; // 這是顯然的 f[i*p[j]] = f[i] * f[p[j]]; } } } }
狄利克雷卷積
咱們知道 $$\varphi(n)=\sum\limits_{d|n}\mu(d)\dfrac{n}{d}$$ 其右端和的形式,在數論中會常常出現。因而乎。。。gc
定義
設$f,g$是兩個數論函數,則他們的狄利克雷卷積$h(n)$也是一個數論函數,由下式給出 $$h(n)=\sum\limits_{d|n}f(d)g\left(\dfrac{n}{d}\right)$$ 簡記爲$h=f*g$。程序
性質
狄利克雷卷積有以下性質
- 交換律:$fg=gf$,這個結論仍是很顯然的。
- 結合律:$(fg)h=f(gh)$。 這是由於 $$\sum\limits_{(ij)k=n}(f(i)g(j))h(k)=\sum\limits_{i(jk)=n}f(i)(g(j)h(k))$$
- $\epsilon*f=f$
- 對於知足$f(1)=\not0$的數論函數$f$,存在惟一的數論函數$g$使得$f*g=\epsilon$,$g$稱爲$f$的狄利克雷逆函數。 逆函數的計算 直接構造 $$g(n)=\dfrac{1}{f(1)}\left([n=1]-\sum\limits_{d|n,d\neq1f(d)}g\left(\dfrac{n}{d}\right)\right)$$ 這樣 $$\begin{aligned} &\sum\limits_{d|n}f(d)g\left(\dfrac{n}{d}\right) \ =&f(1)g(n) + \sum\limits_{d|n,d\neq1}f(d)g\left(\dfrac{n}{d}\right)\ =&[n=1] \end{aligned}$$
經常使用的狄利克雷卷積公式
- $\mu*I=\epsilon$
- $\varphi*I=id$
- $\mu*id=\varphi$
杜教篩
杜教篩能夠經過構造狄利克雷卷積在非線性時間內求積性函數的前綴和。
前置知識
- 數論分塊
- 積性函數
- 莫比烏斯反演
- 狄利克雷卷積
構造方法
給定積性函數$f,g$,設$S(n)=\sum\limits_{i=1}^nf(i)$,則有 $$\begin{aligned} \sum\limits_{i=1}^n(fg)(i)&=\sum\limits_{i=1}^n\sum\limits_{d|i}f(d)g\left(\frac{i}{d}\right) \ &=\sum\limits_{d=1}^ng(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}f(i) \ &=\sum\limits_{d=1}^ng(d)S\left(\lfloor\frac{n}{d}\rfloor\right) \end{aligned}$$ 因爲$g(1)=1$,能夠構造 $$S(n)=g(1)S(n)=\sum\limits_{d=1}^ng(d)S\left(\lfloor\frac{n}{d}\rfloor\right)-\sum\limits_{d=2}^ng(d)S\left(\lfloor\frac{n}{d}\rfloor\right)$$ 而 $$\sum\limits_{d=1}^ng(d)S\left(\lfloor\frac{n}{d}\rfloor\right)=\sum\limits_{i=1}^n(fg)(i)$$ 故 $$S(n)=\sum\limits_{i=1}^n(fg)(i)-\sum\limits_{d=2}^ng(d)S\left(\lfloor\frac{n}{d}\rfloor\right)$$ 若是能夠構造出合適的積性函數$g$,使$\sum\limits_{i=1}^n(fg)(i)$能夠快速計算,就能夠使用數論分塊遞歸地求出$S(n)$。 代碼大概長這個樣子
int S(int n) { int ret = ...; // f*g 的前綴和 for (int l = 2, r; l <= n; l = r+1) { // 數論分塊, 注意 l 從 2 開始 r = n / (n/l); ret -= (sum_g[r]-sum_g[l-1]) * S(n/l); // sum_g 表示 g 的前綴和 } return ret; }
這樣的時間複雜度爲 $$T(n)=\sum\limits_{i=1}^{\sqrt n}O(\sqrt i)+O(\sqrt \frac{n}{i})=O(n^{\frac{3}{4}})$$
時間複雜度的優化
線性篩與杜教篩結合使用。先用線性篩篩出前$m$個數的答案,以後再用杜教篩。 這樣時間複雜度爲 $$T(n)=\sum\limits_{i=1}^{\lfloor\frac{n}{m}\rfloor}\sqrt \frac{n}{i}=O\left(\frac{n}{\sqrt m}\right)$$ 當$m=n^{\frac{2}{3}}$時,$T(n)$取最小值$O(n^{\frac{2}{3}})$ 至於記憶化,能夠手寫哈希表,也能夠用 map 或 unordered_map。 代碼以下
unordered_map<int, int> rec; // 這裏使用 C++11 的 unordered_map int S(n) { if (n <= m) return sum[n]; // sum 爲線性篩預處理的前綴和 if (rec[n]) return rec[n]; int ret = &rec[n]; ret = ...; // f*g 的前綴和 for (int l = 2, r; l <= n; l = r+1) { r = n / (n/l); ret -= (sum_g[r]-sum_g[l-1]) * S(n/l); // sum_g 表示 g 的前綴和 } return ret; }
構造舉例
- $S(n)=\sum\limits_{i=1}^n\varphi(i)$ 由於$\varphiI=id$,因此 $$\begin{aligned} S(n)=I(1)S(n)&=\sum\limits_{i=1}^n(\varphiI)(i)-\sum\limits_{d=2}^nI(d)S\left(\lfloor\frac{n}{d}\rfloor\right)\ &=\sum\limits_{i=1}^nid(i)-\sum\limits_{d=2}^nS\left(\lfloor\frac{n}{d}\rfloor\right)\ &=\dfrac{n(n+1)}{2}-\sum\limits_{d=2}^nS\left(\lfloor\frac{n}{d}\rfloor\right) \end{aligned}$$ 代碼以下
unordered_map<int, int> rec; int S(n) { if (n <= m) return sum[n]; if (rec[n]) return rec[n]; int ret = &rec[n]; ret = n * (n+1) / 2; for (int l = 2, r; l <= n; l = r+1) { r = n / (n/l); ret -= (r-l+1) * S(n/l); } return ret; }
- $S(n)=\sum\limits_{i=1}^n\mu(i)$ 由於$\muI=\epsilon$,因此 $$\begin{aligned} S(n)=I(1)S(n)&=\sum\limits_{i=1}^n(\muI)(i)-\sum\limits_{d=2}^nI(d)S\left(\lfloor\frac{n}{d}\rfloor\right)\ &=\sum\limits_{i=1}^n\epsilon(i)-\sum\limits_{d=2}^nS\left(\lfloor\frac{n}{d}\rfloor\right)\ &=1-\sum\limits_{d=2}^nS\left(\lfloor\frac{n}{d}\rfloor\right) \end{aligned}$$ 代碼以下
unordered_map<int, int> rec; int S(n) { if (n <= m) return sum[n]; if (rec[n]) return rec[n]; int ret = &rec[n]; ret = 1; for (int l = 2, r; l <= n; l = r+1) { r = n / (n/l); ret -= (r-l+1) * S(n/l); } return ret; }
- $S(n)=\sum\limits_{i=1}^n i\cdot\varphi(i)$ 設$f(n)=n\cdot\varphi(n)$,即$S(n)=\sum\limits_{i=1}^nf(i)$ 構造$g(n)=n$,即$g=id$ 由於 $$\begin{aligned} (fg)(n)&=\sum\limits_{d|n}f(d)g\left(\frac{n}{d}\right)\ &=\sum\limits_{d|n}d\cdot\varphi(d)\cdot\dfrac{n}{d}\ &=\sum\limits_{d|n}n\cdot\varphi(d)\ &=n\sum\limits_{d|n}\varphi(d)\ &=n^2 \end{aligned}$$ 因此 $$\sum\limits_{i=1}^n(fg)(i)=\sum\limits_{i=1}^ni^2=\dfrac{n(n+1)(2n+1)}{6}$$ 故 $$\begin{aligned} S(n)=g(1)S(n)&=\sum\limits_{i=1}^n(f*g)(i)-\sum\limits_{d=2}^ng(d)S\left(\lfloor\frac{n}{d}\rfloor\right)\ &=\dfrac{n(n+1)(2n+1)}{6}-\sum\limits_{d=2}^nd\cdot S\left(\lfloor\frac{n}{d}\rfloor\right) \end{aligned}$$ 代碼以下
unordered_map<int, int> rec; int S(n) { if (n <= m) return sum[n]; if (rec[n]) return rec[n]; int ret = &rec[n]; ret = n * (n+1) * (2*n+1) / 6; for (int l = 2, r; l <= n; l = r+1) { r = n / (n/l); ret -= (l+r) * (r-l+1) / 2 * S(n/l); } return ret; }
min_25篩
留個坑吧,應該會填的。。。