符号及基本定义及约定
整除、取模、质数、算术基本定理等的定义。这部分直接 skip 了。
以下无特殊说明则默认变量为正整数,函数为数论函数。
以下用 P \mathbb{P} P 表示素数集合。
数论函数
定义域为正整数的函数被称为数论函数 。
积性函数
若数论函数 f ( x ) f(x) f ( x ) 满足对于任意 a ⊥ b a \bot b a ⊥ b ,有 f ( a b ) = f ( a ) f ( b ) f(ab) = f(a) f(b) f ( a b ) = f ( a ) f ( b ) ,则称 f ( x ) f(x) f ( x ) 为积性函数 。
若数论函数 f ( x ) f(x) f ( x ) 满足对于任意 a , b a, b a , b ,有 f ( a b ) = f ( a ) f ( b ) f(ab) = f(a) f(b) f ( a b ) = f ( a ) f ( b ) ,则称 f ( x ) f(x) f ( x ) 为完全积性函数 。
性质
若 f ( x ) f(x) f ( x ) 和 g ( x ) g(x) g ( x ) 均为积性函数,则以下函数也是积性函数。
h ( x ) = f ( x p ) h(x) = f(x ^ p) h ( x ) = f ( x p ) 。
h ( x ) = f p ( x ) h(x) = f ^ p(x) h ( x ) = f p ( x ) 。
h ( x ) = f ( x ) g ( x ) h(x) = f(x) g(x) h ( x ) = f ( x ) g ( x ) 。
h ( x ) = ∑ d ∣ x f ( d ) g ( x d ) h(x) = \sum _ {d \mid x} f(d) g\left(\dfrac{x}{d}\right) h ( x ) = ∑ d ∣ x f ( d ) g ( d x ) 。
设 x = ∏ p i k i x = \prod p _ i ^ {k _ i} x = ∏ p i k i 。
对于积性函数 f ( x ) f(x) f ( x ) 有 f ( x ) = ∏ f ( p i k i ) f(x) = \prod f(p _ i ^ {k _ i}) f ( x ) = ∏ f ( p i k i ) 。
对于完全积性函数 f ( x ) f(x) f ( x ) 有 f ( x ) = ∏ f ( p i ) k i f(x) = \prod f(p _ i) ^ {k _ i} f ( x ) = ∏ f ( p i ) k i 。
常见积性函数
单位函数 ε ( n ) = [ n = 1 ] \varepsilon(n) = [n = 1] ε ( n ) = [ n = 1 ] 。(完全奇性)
常数函数 1 ( n ) = 1 1(n) = 1 1 ( n ) = 1 。(完全奇性)
除数函数 σ k ( n ) = ∑ d ∣ n d k \sigma _ k(n) = \sum _ {d | n} d ^ k σ k ( n ) = ∑ d ∣ n d k 。特别地,σ 0 ( n ) \sigma _ 0(n) σ 0 ( n ) 通常记作 d ( n ) d(n) d ( n ) 或 τ ( n ) \tau(n) τ ( n ) ,σ 1 ( n ) \sigma _ 1(n) σ 1 ( n ) 通常记作 σ ( n ) \sigma(n) σ ( n ) 。
欧拉函数和莫比乌斯函数后面会说。
素数
素数计数函数:π ( n ) = ∑ i = 1 n [ i ∈ P ] \pi(n) = \sum\limits _ {i = 1} ^ n [i \in \mathbb{P}] π ( n ) = i = 1 ∑ n [ i ∈ P ] 。
结论:π ( n ) ∼ n ln n \pi(n) \sim \dfrac{n}{\ln n} π ( n ) ∼ ln n n 。
Miller-Rabin 素性测试
**素性测试(Primality test)**是一类在 **不对给定数字进行素数分解(prime factorization)**的情况下,测试其是否为素数的算法。——OI Wiki
素性测试有两种,一种为确定性测试,能完全确定一个数是否为素数;另一种为概率性测试,通常比确定性测试快得多,但有极小概率将合数判断为素数。
Fermat 素性测试
利用费马小定理(∀ p ∈ P , a ⊥ p , a p − 1 ≡ 1 ( m o d p ) \forall p \in \mathbb{P}, a \bot p, a ^ {p - 1} \equiv 1 \pmod{p} ∀ p ∈ P , a ⊥ p , a p − 1 ≡ 1 ( m o d p ) ,后面会展开说明),不断选取 a ∈ [ 2 , n ) a \in [2, n) a ∈ [ 2 , n ) 并检验是否满足 a n − 1 ≡ 1 ( m o d n ) a ^ {n - 1} \equiv 1 \pmod{n} a n − 1 ≡ 1 ( m o d n ) 。
若对于合数 n n n ,满足对于任意 a ⊥ n a \bot n a ⊥ n ,有 a n − 1 ≡ 1 ( m o d n ) a ^ {n - 1} \equiv 1\pmod{n} a n − 1 ≡ 1 ( m o d n ) ,则称之为卡迈克尔数(Carmichael Number) ,又称费马伪素数 。
卡迈克尔数是无穷的。(见 OEIS A006931 (拥有 n n n 个质因子的最小的卡迈克尔数))
二次探测定理
对于 p ∈ P p \in \mathbb{P} p ∈ P ,方程 x 2 ≡ 1 ( m o d p ) x ^ 2 \equiv 1 \pmod{p} x 2 ≡ 1 ( m o d p ) 的解为 x ≡ 1 ( m o d p ) x \equiv 1 \pmod{p} x ≡ 1 ( m o d p ) 或 x ≡ p − 1 ( m o d p ) x \equiv p - 1 \pmod{p} x ≡ p − 1 ( m o d p ) 。
实现
考虑结合 Fermat 素性测试和二次探测定理。
首先特判掉 n n n 为偶数的情况,然后有 n − 1 n - 1 n − 1 为偶数。
所以 a n − 1 = ( a n − 1 2 ) 2 ≡ 1 ( m o d n ) a ^ {n - 1} = \left(a ^ {\frac{n - 1}{2}}\right) ^ 2 \equiv 1 \pmod{n} a n − 1 = ( a 2 n − 1 ) 2 ≡ 1 ( m o d n ) 。若 n n n 为素数则必须满足 a n − 1 2 a ^ {\frac{n - 1}{2}} a 2 n − 1 为 1 1 1 或 n − 1 n - 1 n − 1 。
于是,我们将 n − 1 n - 1 n − 1 拆分为 u × 2 t u \times 2 ^ t u × 2 t ,然后令 x = a u x = a ^ u x = a u ,不断平方至多 t t t 次,判断过程是否出现 x = n − 1 x = n - 1 x = n − 1 即可。
在 OI 中,a a a 一般取{ 2 , 325 , 9375 , 28178 , 450775 , 9780504 , 1795265022 } \{2, 325, 9375, 28178, 450775, 9780504, 1795265022\} { 2 , 3 2 5 , 9 3 7 5 , 2 8 1 7 8 , 4 5 0 7 7 5 , 9 7 8 0 5 0 4 , 1 7 9 5 2 6 5 0 2 2 } 或 { 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 } \{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37\} { 2 , 3 , 5 , 7 , 1 1 , 1 3 , 1 7 , 1 9 , 2 3 , 2 9 , 3 1 , 3 7 } (前 12 12 1 2 个质数)即可解决 [ 2 , 2 64 ) [2, 2 ^ {64}) [ 2 , 2 6 4 ) 以内的问题。
注意要取遍里面的数(模 n n n 意义下)而不是只取 ≤ n \le n ≤ n 的。若模 n n n 意义下为 0 0 0 则直接跳过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <cstdio> #include <string> #include <cstring> #include <iostream> #include <algorithm> using namespace std;const int prime[] = {2 , 3 , 5 , 7 , 9 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 };int T;long long n;long long power (long long a, long long b, long long mod) { long long ans = 1 ; while (b) { if (b & 1 ) ans = (__int128)ans * a % mod; a = (__int128)a * a % mod; b >>= 1 ; } return ans % mod; } int Miller_Rabin (long long n) { if (n == 1 ) return 0 ; if (n == 2 ) return 1 ; if (n % 2 == 0 ) return 0 ; long long u = n - 1 , t = 0 ; while (u % 2 == 0 ) u /= 2 , t++; for (int i = 0 ; i < 12 ; i++) { if (prime[i] % n == 0 ) continue ; long long x = power (prime[i] % n, u, n); if (x == 1 ) continue ; int flag = 0 ; for (int j = 1 ; j <= t; j++) { if (x == n - 1 ) {flag = 1 ; break ;} x = (__int128)x * x % n; } if (!flag) return 0 ; } return 1 ; } int main () { scanf ("%d" , &T); while (T--) { scanf ("%lld" , &n); puts (Miller_Rabin (n) ? "YES" : "NO" ); } return 0 ; }
对 n n n 进行 k k k 轮测试的时间复杂度为 O ( k log 3 n ) O(k \log ^3 n) O ( k log 3 n ) ,可以使用 FFT 优化到 O ( k log 2 n log log n log log log n ) O(k \log ^ 2 n \log \log n \log \log \log n) O ( k log 2 n log log n log log log n ) 。
数论分块
用于快速求解 ∑ i = 1 n f ( i ) g ( n i ) \sum _ {i = 1} ^ n f(i) g\left(\dfrac{n}{i}\right) ∑ i = 1 n f ( i ) g ( i n ) 式的式子。
结论 1:∀ n ∈ N + , ∣ ⌊ n d ⌋ ∣ d ∈ N + , d ≤ n ∣ ≤ ⌊ 2 n ⌋ \forall n \in \mathbb{N _ +}, \left|\left\lfloor\dfrac{n}{d}\right\rfloor \mid d \in \mathbb{N _ +}, d \le n\right| \le \lfloor2\sqrt{n}\rfloor ∀ n ∈ N + , ∣ ∣ ∣ ∣ ⌊ d n ⌋ ∣ d ∈ N + , d ≤ n ∣ ∣ ∣ ∣ ≤ ⌊ 2 n ⌋ 。
证明:
对于 d ≤ n d \le \sqrt{n} d ≤ n ,⌊ n d ⌋ \left\lfloor\dfrac{n}{d}\right\rfloor ⌊ d n ⌋ 显然只有 n \sqrt{n} n 种取值。
对于 d > n d > \sqrt{n} d > n ,有 n d ≤ n \dfrac{n}{d} \le \sqrt{n} d n ≤ n ,所以 ⌊ n d ⌋ \left\lfloor\dfrac{n}{d}\right\rfloor ⌊ d n ⌋ 也只有 n \sqrt{n} n 种取值。
故总共只有 ⌊ 2 n ⌋ \lfloor2\sqrt{n}\rfloor ⌊ 2 n ⌋ 种取值。
证毕。
结论 2:对于任意 n , i , i ≤ n n, i, i \le n n , i , i ≤ n ,满足 ⌊ n i ⌋ = ⌊ n j ⌋ \left\lfloor\dfrac{n}{i}\right\rfloor = \left\lfloor\dfrac{n}{j}\right\rfloor ⌊ i n ⌋ = ⌊ j n ⌋ 的最大的 j j j 为 ⌊ n ⌊ n i ⌋ ⌋ \left\lfloor\dfrac{n}{\lfloor\frac{n}{i}\rfloor}\right\rfloor ⌊ ⌊ i n ⌋ n ⌋ 。
证明:
⌊ n i ⌋ = ⌊ n j ⌋ ≤ n j ⟹ j ≤ n ⌊ n i ⌋ \left\lfloor\dfrac{n}{i}\right\rfloor = \left\lfloor\dfrac{n}{j}\right\rfloor \le \dfrac{n}{j} \implies j \le \dfrac{n}{\lfloor\frac{n}{i}\rfloor} ⌊ i n ⌋ = ⌊ j n ⌋ ≤ j n ⟹ j ≤ ⌊ i n ⌋ n 。由于 j j j 要最大,所以 j = ⌊ n ⌊ n i ⌋ ⌋ j = \left\lfloor\dfrac{n}{\lfloor\frac{n}{i}\rfloor}\right\rfloor j = ⌊ ⌊ i n ⌋ n ⌋ 。证毕。
由于 ⌊ n i ⌋ \left\lfloor\dfrac{n}{i}\right\rfloor ⌊ i n ⌋ 值相同的 i i i 是连续的,且一共只有 O ( n ) O(\sqrt{n}) O ( n ) 种取值,故我们可以枚举所有 ⌊ n i ⌋ \left\lfloor\dfrac{n}{i}\right\rfloor ⌊ i n ⌋ 的取值和对应的 i i i 的区间,如果能快速求出 f f f 的前缀和,就可以通过 O ( n ) O(\sqrt{n}) O ( n ) 次计算得到结果。
伪代码大概长这样:
1 Method. 2 Function sqrtDecomposition ( ) 3 l ← 1 4 r ← 0 5 while l ≤ n do: 6 r ← ⌊ n ⌊ n l ⌋ ⌋ 7 a n s ← a n s + ( ∑ i = l r f ( i ) ) ⋅ g ( ⌊ n l ⌋ ) 8 l ← r + 1 9 endwhile 10 return a n s 11 Endfunction \begin{array}{l|l}
1 & \textbf{Method.} \\
2 & \textbf{Function } \text{sqrtDecomposition}()\\
3 & \qquad l \gets 1 \\
4 & \qquad r \gets 0 \\
5 & \qquad \textbf{while } l \le n \textbf{ do:} \\
6 & \qquad \qquad r \gets \left\lfloor\dfrac{n}{\lfloor\frac{n}{l}\rfloor}\right\rfloor \\
7 & \qquad \qquad ans \gets ans + \left(\sum\limits _ {i = l} ^ r f(i)\right) \cdot g\left(\left\lfloor\dfrac{n}{l}\right\rfloor\right) \\
8 & \qquad \qquad l \gets r + 1 \\
9 & \qquad \textbf{endwhile} \\
10 & \qquad \textbf{return } ans \\
11 & \textbf{Endfunction}
\end{array}
1 2 3 4 5 6 7 8 9 1 0 1 1 Method. Function sqrtDecomposition ( ) l ← 1 r ← 0 while l ≤ n do: r ← ⌊ ⌊ l n ⌋ n ⌋ a n s ← a n s + ( i = l ∑ r f ( i ) ) ⋅ g ( ⌊ l n ⌋ ) l ← r + 1 endwhile return a n s Endfunction
若计算 f f f 的前缀和和 g g g 的某个点值都是 O ( 1 ) O(1) O ( 1 ) 的,则时间复杂度为 O ( n ) O(\sqrt{n}) O ( n ) 。