常见的数学算法包括:快速幂、矩阵运算、欧拉函数与欧拉定理、欧几里得算法及其扩展、中国剩余定理(CRT)、高斯消元、乘法逆元、组合数求解、质数筛选、博弈论模型及其他杂项技巧。
快速幂
快速幂的目的就是做到快速求幂,假设我们要求 a^b,按照朴素算法就是把 a 连乘 b 次,这样一来时间复杂度是O(b)也即是O(n)级别,快速幂能做到O(log n).
例如当 b=11 时, 将 b 写成二进制格式 1011,即1 * 23 + 0 * 22 + 1 * 21 + 1 * 20:
a11 = a(20 + 21 + 23)
1 | int qpow(int a, int b) { |
另附快速乘法 (mod p)意义下
1 | long qmul(long a,long b,long mod) { |
矩阵
- 矩阵快速幂 矩阵快速幂就是把快速幂里的乘法换成矩阵乘法 求解 A = matK mat为原矩阵,A是答案
1 | static long n, N, k, P = (long)1e9+7; |
- 矩阵加速(数列)板子
那么我们可以维护两个我们首先要确定目标矩阵。下面这个矩阵就是我想要的矩阵.
并且有递推式 F[i] = F[i-1]+F[i-3] 那么这个矩阵每个元素就可以表示成如下
通过每一项的系数可以得出Mi与Mi − 1递推关系
则写个两三项可发现 F[i] = Ai − 3[0][0] + Ai − 3[0][1] + Ai − 3[0][2]
用矩阵快速幂求出Ai − 3即可
欧拉函数&欧拉定理
- 欧拉函数 Euler’s totient function,即 φ(n) ,表示的是小于等于
n 和 n 互质的数的个数。
其中 p1, p2, p3, ⋯, pk 是 n 的所有不重复的素因子。 对应下方代码中 ans = ans/i * (i − 1);
1 | int euler_phi(int n) { |
- 欧拉定理 若 gcd (a, m) = 1 ,则 aφ(m) ≡ 1( mod m) 。
- 拓展欧拉定理
筛法求欧拉函数
注意到在线性笑中,每一个合数都是被最小的质因子笑掉。比如设 p1 是 n 的最小质因子,
那如果 n′ mod p1 ≠ 0 呢,这时 n′ 和 p1 是互质的,根据欧拉函数性质,我们有:
1 | static void euler(int n){ |
欧几里得定理
辗转相除法
- 最大公因数 GCD(a, b)
- 最小公倍数 LCM(a, b) = a/GCD(a, b)*b
1 | int gcd(int a, int b){ |
1 | int lcm(int a, int b){ |
Binary GCD
a, b 均为偶数。对于这种情况,显然 2 是公约数之一,直接将两个数都除以 2 ,再递归 下去求
即可,a, b 中有且仅有一个偶数。不妨设 a 是偶数,那么显然 2 不是公约数之一,直接将 a 除 以 2 求
即可,a, b 均为奇数。不妨设 a > b ,那么有
。 设 a = k1gcd (a, b), b = k2gcd (a, b) 且 gcd (k1, k2) = 1 ,先考虑证明 gcd (a − b, b) = gcd (a, b) a − b = (k1 − k2)gcd (a, b), b = k2gcd (a, b) ,那么有gcd (a − b, b) = gcd (k1 − k2, k2) ⋅ gcd (a, b) , 即证 gcd (k1 − k2, k2) = 1 考虑反证法,假设 gcd (k1 − k2, k2) = m 其中 m ∈ ℕ*, m > 1 ,再设 k1 − k2 = t1m, k2 = t2m 就有 k1 = (t1 + t2)m ,那么 gcd (k1, k2) = m ⋅ gcd (t1 + t2, t2) > 1 与 k1, k2 互质矛 盾,得证 gcd (k1 − k2, k2) = 1
1 | int bgcd(int a, int b){ |
拓展欧几里得
贝祖定理:a、b 是整数,那么一定存在整数 x、y 使得 ax+by=gcd(a,b)。
- 如果 ax+by=1 有解,那么gcd(a,b)=1;
解方程:ax+by=m (a, b, m 均为正整数)
首先解 ax+by=gcd(a, b). 递归边界:b = 0, a = gcd(a, b); 显然 x = 1, y = 0. 递归推导:当前层为 a, b; 求解 ax+by=gcd(a, b). 由递归下一层 b, a%b, 可得 bx1+(a%b)y1=gcd(a, b).
由于 a%b = a - (a/b)b. —(3) 联立(2)(3) 解得 ay1 + b(x1 - (a/b)y1) = gcd(a, b). 对比 (1) 式 解得 x = y1, y = x1-a/b*y1.
1 | static int x, y; |
解 ax+by=m 若 方程 ax+by=m 的一组整数解为(x,y). 则它的任意整数解都可以写成(x + k _ b’,y - k _ a’)。 其中 a’ = a / gcd(a,b), b’ = b / gcd(a,b),k 取任意整数
ax+by=m 有解的充分条件 ==gcd(a,b) | m== //m 能整除gcd(a, b)
设(x1, y1)是 ax+by=gcd(a, b) 解
则 ax+by=m 的解为 x = x1c/gcd(a,b), y = y1c/gcd(a,b),
1 | public static void main(String[] args){ |
中国剩余定理(CRT)
求解如下形式的一元线性同余方程组==最小正整数解==。(注意b1,b2,⋯,bk两两不一定互质,拓展版)
设两个方程分别是 x ≡ a1( mod m1)、x ≡ a2( mod m2) ; 将它们转化为不定方程: x = m1p + a1 = m2q + a2 ,其中 p, q 是整数,则有 m1p − m2q = a2 − a1 由 裴蜀定理,当 a2 − a1 不能被 gcd (m1, m2) 整除时,无解; 其他情况下,可以通过扩展欧几里得算法 解出来一组可行解 (p, q) ; 则原来的两方程组成的模方程组的解为 x ≡ b( mod M) ,其中 b = m1p + a1,M = lcm (m1, m2)
1 | static void solve() throws Exception { |
高斯消元(解一次方程组)
1.选择一个尚未被选过的未知数作为主元,选择一个包含这个主元的方程。
2.通过加减消元,消掉其它方程中的这个未知数。
3.重复以上步骤,直到把每一行都变成只有一项有系数。(转换为对角矩阵)
4.输出时记得除以方程主元的系数。
1 | static double[][] a = new double[N][N]; //double 存 |
乘法逆元
定义:如果一个线性同余方程 ax ≡ 1
(
- 快速幂 费马小定理(==此时 p 一定为素数==)
1 | int qpow(long a, long b) { |
线性算法求一连串数字的逆元(==此时 p 一定是素数==)
用于求一连串数字对于一个 mod p的逆元。洛谷 P3811
只能用这种方法,别的算法都比这些要求一串要慢。
首先我们有一个, 1−1 ≡ 1 (mod p)
然后设 p = k * i + r, (1 < r < i < p)也就是 k是 p/i的商,r 是余数 。
再将这个式子放到 (mod p)意义下就会得到:
k * i + r ≡ 0 (mod p)
然后乘上i−1, r−1就可以得到:
于是,我们就可以从前面推出当前的逆元了。
1 | inv[1] = 1; |
组合数(模P意义下)
- 打表(杨辉三角)
1 | for(int i=0;i<=n;i++){ |
预处理 O(nm) 查询 O(1)
- 乘法逆元+快速幂+阶乘
1 | int pow(int x,int y){...} //快速幂 |
预处理 O(n) 查询 O(log p)
分解质因数
1 | static void solve() throws Exception{ |
质数
埃氏筛 O(Nlog2log2N)
1
2
3
4
5
6
7
8
9
10
11
12
13bool numlist[100000001]; //true:合数 false:素数
int prime[20000001]; //存储从小到大的素数(2,3,5,···)
int cnt; //存储素数数量
void work(int n){
for(int i=2; i<=n; i++){
if(numlist[i]==false){
prime[++cnt] = i ;
for(int j=i; i*j<=n; j++)
numlist[i*j] = true;
}
}
return;
}欧拉筛 O(N)
1
2
3
4
5
6
7
8
9
10
11
12
13bool numlist[100000001];
int prime[20000001], cnt;
void work(int n){
for(int i=2; i<=n; i++){
if(numlist[i]==false) prime[++cnt] = i ;
for(int j=1; j<=cnt&&i*prime[j]<=n; j++){
numlist[i*prime[j]] = true ;
if(i%prime[j]==0)
break;
}
}
return;
}
博弈论
nim 游戏
nim 游戏的规则是这样的:地上有 n 堆石子,每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这 n 堆石子的数量,他想知道是否存在先手必胜的策略。
1 | int t = 0; |
杂项
- 三角形面积
海伦公式:假设在平面内,有一个三角形,边长分别为 a、b、c ,三角形的面积 S 可由以下公式 求得:
而公式里的 p 为半周长 (周长的一半) :
向量积:
- 任意多边形面积
对于任意一个多边形,如果已知其各个顶点的坐标 A1(x1, y1), A2(x2, y2), …, An(xn, yn) ,那么这个多边形的面积为:
其中 xn + 1 = x1, yn + 1 = y1。