闲话
我谔谔,第一稿写了一大部分没有保存。
质数 & 约数
质数判定
试除法
没啥好说,会的都会,复杂度 O ( n ) O(\sqrt{n}) O ( n )
1 2 3 4 5 6 7 bool isprime (int x) { if (x == 1 ) return false ; for (int i = 2 ; i <= n / i; i ++) if (x % i == 0 ) return false return true ; }
Fermat 素性检验
通过反着使用费马小定理,多次换底增加正确性。
但是可以被卡迈尔数完美卡掉。
Miller Rabin 素性检验
【朝夕的ACM笔记】数论-Miller Rabin素数判定 - 知乎 (zhihu.com)
在 Fermat 素性检验基础上,引入二次探测定理(对于质数 p p p ,x 2 ≡ 1 ( m o d p ) x^2\equiv1\pmod p x 2 ≡ 1 ( mod p ) 的解仅可能为 x ≡ 1 ( m o d p ) x\equiv 1\pmod p x ≡ 1 ( mod p ) 或 x ≡ p − 1 ( m o d p ) x\equiv p-1\pmod p x ≡ p − 1 ( mod p ) )以增加正确性。
对于 Fermat 素性检验中,a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1\pmod p a p − 1 ≡ 1 ( mod p ) ,可以解出 a p − 1 2 ≡ 1 ( m o d p ) a^{\frac{p-1}{2}}\equiv 1\pmod p a 2 p − 1 ≡ 1 ( mod p ) 或 a p − 1 2 ≡ p − 1 ( m o d p ) a^{\frac{p-1}{2}}\equiv p-1\pmod p a 2 p − 1 ≡ p − 1 ( mod p ) .
以此类推,即可验证 p p p 是否为质数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int qmul (int a, int b, int p) {return (a * b - (int )((long double ) a / p * b) * p + p) % p;}int qpow (int a, int b, int p) {int res = 1 ; while (b) {if (b & 1 ) res = qmul (res, a, p); a = qmul (a, a, p); b >>= 1 ;} return res;}bool miller_rabin (int p) { if (p < 3 || p % 2 == 0 ) return p == 2 ; int u = p - 1 , t = 0 ; while (u % 2 == 0 ) u /= 2 , t ++; int used[] = {2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 }; for (auto a : used) { if (a >= p) break ; int v = qpow (a, u, p); if (v == 1 || v == p - 1 ) continue ; for (int j = 1 ; j <= t; j ++) { v = qmul (v, v, p); if (v == p - 1 && j != t) {v = 1 ; break ;} if (v == 1 ) return false ; } if (v != 1 ) return false ; } return true ; }
分解质因数
试除法
没啥好说,会的都会,复杂度 O ( n ) O(\sqrt{n}) O ( n ) .
1 2 3 4 5 6 7 8 9 10 11 12 void divdie (int n) { for (int i = 2 ;i <= n / i; i ++) if (n % i == 0 ) { int s = 0 ; while (n % i == 0 ) n /= i, s ++; cout << i << " " << s << endl; } if (n > 1 ) cout << n << " " << 1 << endl; }
筛法
比较鸡肋……复杂度比上一个稍快一点点。
Pollard Rho 大数分解
前置:生日悖论(简单来讲就是你选两个来的答案的概率会比一个高)。
生成两个随机数,尝试它们的差与被分解数的 gcd.
利用一种特殊的随机函数 f ( x ) = ( x 2 + c ) m o d p f(x)=(x^2+c)\mod p f ( x ) = ( x 2 + c ) mod p ,由于模数的限制,此函数一定会出现循环,有因为是混循环,长得像 ρ \rho ρ 所以得名 /kk.
实现时,由于 gcd 的 O ( log n ) O(\log{n}) O ( log n ) 的复杂度,我们考虑将差累乘起来,每隔一段时间 gcd 一下,这样便可以大大降低复杂度(由于前人的口胡 经验,我们每 127 次 gcd 一下)。
具体可以搭配 Miller Rabin 与 dfs 实现分解质因数等。
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 int qmul (int a, int b, int p) {return (a * b - (int )((long double ) a / p * b) * p + p) % p;}int f (int x, int c, int p) {return (qmul (x, x, p) + c) % p;}#define gcd __gcd int pollar_rho (int p) { if (p % 2 == 0 ) return 2 ; #define step 127 bool isfind = false ; int c, r1, r2, pd = 1 , tp, res = 0 ; while (!isfind) { c = rand () % (p - 1 ) + 1 , r1 = r2 = 0 ; do { for (int i = 1 ; i <= step; i ++) { r1 = f (r1, c, p), r2 = f (f (r2, c, p), c, p); if (r1 == r2 || !(tp = qmul (pd, abs (r2 - r1), p))) break ; pd = tp; } res = gcd (pd, p); if (res > 1 ) isfind = true ; } while (!isfind && r1 != r2); } return res; }
比较乱搞的东西qwq
设 x = q 1 c 1 q 2 c 2 … q k c k x = q_1^{c_1}q_2^{c_2}\dots q_k^{c_k} x = q 1 c 1 q 2 c 2 … q k c k ,则有:
约数个数
( c 1 + 1 ) ( c 2 + 1 ) … ( c k + 1 ) (c_1+1)(c_2+1)\dots (c_k+1) ( c 1 + 1 ) ( c 2 + 1 ) … ( c k + 1 )
约数之和
( p 1 0 + p 1 1 + ⋯ + p 1 c 2 ) ( p 2 0 + p 2 1 + ⋯ + p 1 c 2 ) … ( p k 0 + p k 1 + ⋯ + p 1 c k ) (p_1^0+p_1^1+\dots +p_1^{c_2})(p_2^0+p_2^1+\dots +p_1^{c_2})\dots (p_k^0+p_k^1+\dots +p_1^{c_k}) ( p 1 0 + p 1 1 + ⋯ + p 1 c 2 ) ( p 2 0 + p 2 1 + ⋯ + p 1 c 2 ) … ( p k 0 + p k 1 + ⋯ + p 1 c k )
过于简单(
欧拉函数
φ ( n ) \varphi(n) φ ( n ) 表示 1 ∼ n − 1 {1}\sim n-1 1 ∼ n − 1 中,与 n n n 互质的数量。
将 n n n 分解为 n = q 1 c 1 q 2 c 2 … q k c k n = q_1^{c_1}q_2^{c_2}\dots q_k^{c_k} n = q 1 c 1 q 2 c 2 … q k c k
φ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) … ( 1 − 1 p k ) \varphi(n)=n(1-\dfrac{1}{p_1})(1-\dfrac{1}{p_2})\dots(1-\dfrac{1}{p_k}) φ ( n ) = n ( 1 − p 1 1 ) ( 1 − p 2 1 ) … ( 1 − p k 1 )
那么考虑 n n n 与 n ′ = n × p n'=n\times p n ′ = n × p ,p p p 为质数,有:
φ ( n ′ ) = { φ ( n ) × p × ( 1 − 1 p ) = φ ( n ) × ( p − 1 ) ( p ∤ n ) φ ( n ) × p ( p ∣ n ) \varphi(n')=\begin{cases}
\varphi(n)\times p\times (1-\dfrac{1}{p})=\varphi(n)\times (p-1)~~(p\nmid n)\\
\varphi(n)\times p~~(p\mid n)
\end{cases}
φ ( n ′ ) = ⎩ ⎨ ⎧ φ ( n ) × p × ( 1 − p 1 ) = φ ( n ) × ( p − 1 ) ( p ∤ n ) φ ( n ) × p ( p ∣ n )
那么我们就可以在标记的过程中递推求得欧拉函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const int N = ;bool st[N];int phi[N];vector<int > p; void getprime (int n) { for (int i = 2 ; i <= n; i ++) { if (!st[i]) p.push_back (i), phi[i] = i - 1 ; for (auto v : p) { if (v * i > n) break ; st[v * i] = 1 ; if (i % v == 0 ) {phi[i * v] = phi[i] * v; break ;} else phi[i * v] = phi[i] * (v - 1 ); } } }
同余
性质
a ≡ a ( m o d p ) a\equiv a\pmod p a ≡ a ( mod p )
a ≡ b ( m o d p ) a\equiv b\pmod p a ≡ b ( mod p ) 时,b ≡ a ( m o d p ) b\equiv a\pmod p b ≡ a ( mod p )
a ≡ b ( m o d p ) a\equiv b\pmod p a ≡ b ( mod p ) 且 b ≡ c ( m o d p ) b\equiv c\pmod p b ≡ c ( mod p ) 时,a ≡ c ( m o d p ) a\equiv c\pmod p a ≡ c ( mod p )
a ≡ b ( m o d p ) a\equiv b\pmod p a ≡ b ( mod p ) 时,a + c ≡ b + c ( m o d p ) a+c\equiv b+c\pmod p a + c ≡ b + c ( mod p )
a ≡ b ( m o d p ) a\equiv b\pmod p a ≡ b ( mod p ) 时,a c ≡ b c ( m o d p ) ac\equiv bc\pmod p a c ≡ b c ( mod p )
a ≡ b ( m o d p ) a\equiv b\pmod p a ≡ b ( mod p ) 时,a c ≡ b c ( m o d p ) a^c\equiv b^c\pmod p a c ≡ b c ( mod p )
a c ≡ b c ( m o d p ) ac\equiv bc\pmod p a c ≡ b c ( mod p ) 且 ( c , p ) = 1 (c, p)=1 ( c , p ) = 1 时,a ≡ b ( m o d p ) a\equiv b\pmod p a ≡ b ( mod p )
a + b ≡ a m o d p + b m o d p ( m o d p ) a+b\equiv a\bmod p+b\bmod p\pmod p a + b ≡ a mod p + b mod p ( mod p )
a b ≡ ( a m o d p ) ( b m o d p ) ( m o d p ) ab\equiv (a\bmod p)(b\bmod p)\pmod p ab ≡ ( a mod p ) ( b mod p ) ( mod p )
a ≡ b ( m o d p 1 ) a\equiv b\pmod{p_1} a ≡ b ( mod p 1 ) 且 a ≡ b ( m o d p 2 ) a\equiv b\pmod{p_2} a ≡ b ( mod p 2 ) 时,a ≡ b ( m o d [ p 1 , p 2 ] ) a\equiv b\pmod{[p_1,p_2]} a ≡ b ( mod [ p 1 , p 2 ])
简化剩余系
不会 不知道用途在哪,爬了爬了[揩汗]
基本定理
欧拉定理:若 ( a , p ) = 1 (a,p)=1 ( a , p ) = 1 ,一定有 a φ ( p ) ≡ 1 ( m o d p ) a^{\varphi(p)}\equiv 1\pmod p a φ ( p ) ≡ 1 ( mod p ) .
费马小定理:若 p p p 为质数,则对于任意整数 a a a ,有 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1\pmod p a p − 1 ≡ 1 ( mod p ) .
扩展欧拉定理:若 b ≥ φ ( p ) b\ge \varphi(p) b ≥ φ ( p ) ,则有 a b ≡ a b m o d φ ( p ) + φ ( p ) ( m o d p ) a^b\equiv a^{b\bmod\varphi(p)+\varphi(p)}\pmod p a b ≡ a b mod φ ( p ) + φ ( p ) ( mod p )
威尔逊定理:若 p p p 为质数,则 ( p − 1 ) ! ≡ p − 1 ( m o d p ) (p-1)!\equiv p-1\pmod p ( p − 1 )! ≡ p − 1 ( mod p ) .
至于用途……降幂是主要的吧
比如 ( 2 2 2 … ) m o d p (2^{2^{2^{\dots}}})\bmod p ( 2 2 2 … ) mod p 显然满足扩展欧拉定理,那么令 f ( a ) = 2 f ( a ) f(a)=2^{f(a)} f ( a ) = 2 f ( a ) ,则
2 f ( a ) ≡ 2 f ( a ) m o d φ ( p ) + φ ( p ) ( m o d p ) 2^{f(a)}\equiv 2^{f(a)\bmod\varphi(p)+\varphi(p)}\pmod p
2 f ( a ) ≡ 2 f ( a ) mod φ ( p ) + φ ( p ) ( mod p )
然后观察指数,发现 f ( a ) m o d φ ( p ) f(a)\bmod\varphi(p) f ( a ) mod φ ( p ) 实际上是 2 f ( a ) m o d φ ( p ) {2}^{f(a)}\bmod\varphi(p) 2 f ( a ) mod φ ( p ) ,那就一眼递归。然后 φ ( p ) \varphi(p) φ ( p ) 是会一直缩小直到 1,这是递归边界。
贴下代码:
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 #include <bits/stdc++.h> #define int long long #define FH signed using namespace std;const int N = 1e7 + 10 ;bool st[N];int phi[N];vector<int > p; void getprime (int n) { for (int i = 2 ; i <= n; i ++) { if (!st[i]) p.push_back (i), phi[i] = i - 1 ; for (auto v : p) { if (v * i > n) break ; st[v * i] = 1 ; if (i % v == 0 ) {phi[i * v] = phi[i] * v; break ;} else phi[i * v] = phi[i] * (v - 1 ); } } } int qmul (int a, int b, int p) {return (a * b - (int )((long double ) a / p * b) * p + p) % p;}int qpow (int a, int b, int p) {int res = 1 ; while (b) {if (b & 1 ) res = qmul (res, a, p); a = qmul (a, a, p); b >>= 1 ;} return res;}int dfs (int p) { if (p == 1 ) return 0 ; return qpow (2 , dfs (phi[p]) + phi[p], p); } FH main () { ios::sync_with_stdio (0 ); cin.tie (0 ); cout.tie (0 ); getprime (N - 1 ); int p; while (cin >> p) cout << dfs (p) << "\n" ; return 0 ; }
扩展欧几里得算法
用于求得形如 a x + b y = ( a , b ) ax+by=(a, b) a x + b y = ( a , b ) 的方程的一组特解。
觉得这张图讲的特别好
鸽鸽 寒假的以后找机会补吧。
其他
数论分块
对于常数 n n n ,使得式子
⌊ n i ⌋ = ⌊ n j ⌋ \left\lfloor\frac ni\right\rfloor=\left\lfloor\dfrac nj\right\rfloor
⌊ i n ⌋ = ⌊ j n ⌋
成立的最大的满足 i ≤ j ≤ n i\leq j\leq n i ≤ j ≤ n 的 j j j 的值为 ⌊ n ⌊ n i ⌋ ⌋ \left\lfloor\dfrac n{\lfloor\frac ni\rfloor}\right\rfloor ⌊ ⌊ i n ⌋ n ⌋ 。即值 ⌊ n i ⌋ \left\lfloor\dfrac ni\right\rfloor ⌊ i n ⌋ 所在的块的右端点为 ⌊ n ⌊ n i ⌋ ⌋ \left\lfloor\dfrac n{\lfloor\frac ni\rfloor}\right\rfloor ⌊ ⌊ i n ⌋ n ⌋ 。
同时可以证明,这样的块的数量总是在 n \sqrt n n 级别的。
对于需要同时满足 ⌊ a 1 i ⌋ \left\lfloor\dfrac {a_1}i\right\rfloor ⌊ i a 1 ⌋ 、⌊ a 2 i ⌋ ⋯ ⌊ a n i ⌋ \left\lfloor\dfrac {a_2}i\right\rfloor\cdots\left\lfloor\dfrac {a_n}i\right\rfloor ⌊ i a 2 ⌋ ⋯ ⌊ i a n ⌋ 时,当前块的右端点为 min j = 1 n { ⌊ a j i ⌋ } \min\limits_{j=1}^n\{\left\lfloor\dfrac {a_j}i\right\rfloor\} j = 1 min n { ⌊ i a j ⌋ } 。
莫比乌斯反演
出于这又臭又长的篇幅,请在这看->莫比乌斯反演学习笔记
拉格朗日插值
额……比较感性的算法,说真的,模板是小学生水平。
给出 n n n 个点 ( x 1 , y 1 ) , ( x 2 , y 2 ) … ( x n , y n ) (x_1,y_1),(x_2,y_2)\dots(x_n,y_n) ( x 1 , y 1 ) , ( x 2 , y 2 ) … ( x n , y n ) ,由这 n n n 个点可以确定一个多项式 f ( x ) f(x) f ( x ) ,求 f ( k ) f(k) f ( k ) 。
显然我们可以很轻松的构造他:f ( x ) = ∑ i = 1 n ∏ j = 1 , i ≠ j n x − x j x i − x j y i \displaystyle f(x)=\sum_{i=1}^{n}\prod_{j=1,i\ne j}^{n}\frac{x-x_j}{x_i-x_j}y_i f ( x ) = i = 1 ∑ n j = 1 , i = j ∏ n x i − x j x − x j y i ,这是显然的,n 2 n^2 n 2 解决就好了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void exgcd (int a, int b, int &x, int &y) {if (b == 0 ) {x = 1 , y = 0 ; return ;} exgcd (b, a % b, y, x); y -= a / b * x;}int inv (int a, int p) {int x, y; exgcd (a, p, x, y); return (x % p + p) % p;}int gety (int x[], int y[], int n, int k, int mod) { int res = 0 ; for (int i = 1 ; i <= n; i ++) { int a = 1 , b = 1 ; for (int j = 1 ; j <= n; j ++) if (i != j) a = a * (((k - x[j]) % mod + mod) % mod) % mod, b = b * (((x[i] - x[j]) % mod + mod) % mod) % mod; res = (res + y[i] * a % mod * inv (b, mod) % mod) % mod; } return res; }