基础数论
1 数论基础
1.1 取模
\((a+b)\mod c=(a\mod c + b\mod c)\mod c\)
\((a-b)\mod c=(a\mod c - b\mod c)\mod c\)
\((a\times b)\mod c=(a\mod c \times b\mod c)\mod c\)
1.2 整除
若存在整数 \(c\) 使得 \(a\times c =b\),则说 \(b\) 是 \(a\) 的倍数,\(a\) 是 \(b\) 的因数,记作 \(a\mid b\),若不存该整数则记作 \(a\nmid b\)。
整除的性质:
- \(a\mid b\iff\pm a\mid\pm b\).
- \(a\mid b \and b\mid c \iff a\mid c\)。
- \(a\mid b \and b\mid a \iff \left|a\right|=\left|b\right|\)。
1.3 最大公因数
1.3.1 更相减损术
平均时间复杂度为 \(O(\log n)\),最坏时间复杂度为 \(O(n)\)。
虽然通常情况下时间复杂度较高,但是在处理大整数(高精度)时,运行效率可能较高。
int gcd(int a,int b){
if(a == b){
return a;
}
return gcd(min(a,b),abs(a-b));
}
注意:当 \(a\) 和 \(b\) 相差过大时,代码的运行速度会非常满,甚至可以卡到 \(O(\max(a,b))\) 左右。
1.3.2 辗转相除法
时间复杂度为 \(O(\log n)\)。
代码简洁,是最常用的 \(\gcd\) 算法。
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
1.3.3 Binary GCD
虽然,该算法的时间复杂度同样也是 \(O(\log n)\),但绝大部分情况下,所需的时间要小于辗转相除法。
int gcd(int a,int b){
int aa = __builtin_ctz(a);
int bb = __builtin_ctz(b);
int c = min(aa,bb);
b >>= bb;
while(a){
a >>= aa;
int t = a-b;
aa = __builtin_ctz(a-b);
b = min(a,b);
a = abs(t);
}
return b<<c;
}
1.4 快速幂
int pow(int a,int b,int p){
int ans = 1;
while(b){
if(b&1){
ans *= a;
ans %= p;
}
a *= a;
a %= p;
b >> 1;
}
return ans;
}
1.5 互质
若 \(\gcd(a,b)=1\),则称 \(a\) 与 \(b\) 互质。
1.6 质数
1.6.1 相关定义
- 质数和合数:若 \(\forall i\in[2,n-1],i\nmid n\),则称正整数 \(n\) 为质数(或素数),否则则称 \(n\) 为合数,特别地,\(1\) 为合数。
- 质因数:若 \(i\in P\and i\mid n\),则称 \(i\) 时 \(n\) 的质因数。
1.6.2 判断质数
1.6.2.1 暴力枚举
时间复杂度 \(O(\sqrt n)\)。
bool is_prime(int n){
for(int i=2;i*i<=n;i++){
if(n%i == 0){
return false;
}
}
return true;
}
1.6.2.2 Millar Robin
\(n\in P \implies a^d\equiv1\pmod n \or i\in [0,r),a^{d\times 2^i}\equiv n-1\pmod n\)。
bool millar_robin(int n,int a){
int d = n-1,r=0;
while(!(d&1)){
d >>= 1,r++;
}
int x = pow(a,d,n);
if(x == 1){
return true;
}
for(int i=0;i<r;i++){
if(x = n-1){
return true;
}
x = 1ll*x*x%n;
}
return false;
}
int prime[] = {2,3,5,7,13,23,37,73};
bool is_prime(int n){
if(n < 2){
return false;
}
if(int i=0;i<8;i++){
int a = prime[i];
if(!millar_robin(n,a)){
return false;
}
}
return true;
}
1.6.2.3分解因数
for(int i=2;i*i<=a;i++){
if(a%i == 0){
cnt ++;
prime[cnt] = i;
while(a%i == 0){
num[cnt]++;
a /= i;
}
}
}
if(x != 1){
cnt ++;
prime[cnt] = x;
num[cnt] = 1;
}
1.7 约数
1.7.1 性质
- 约数个数定理:若正整数 \(n=\prod_{i=1}^sp_i^{a_i}\),则其约数个数为 \(\prod_{i=1}^s(a_i+1)\)。
- 约数和定理:若正整数 \(n=\prod_{i=1}^sp_i^{a_i}\),则其约数和为 $\prod_{i=1}^s \sum_{j=0}{a_i}p_ij $。
1.7.2 线性筛法
1.7.2.1 约数个数
vector<int> prime;
int a[N],d[N];
bool vis[N];
void solve(int n){
d[1] = 1;
for(int i=2;i<=n;i++){
if(!vis[i]){
prime.push_back(i);
a[i] = 1,d[i] = 2;
}else{
for(auto j:prime){
if(i*j > n){
break;
}
vis[i*j] = true;
if(i%j == 0){
a[i*j] = a[i] + 1;
d[i*j] = d[i] / a[i*j] * (a[i*j]+1);
break;
}else{
a[i*j] = 1;
d[i*j] = d[i] * 2;
}
}
}
}
}
1.7.2.2 约数和
void solve(int n){
s[1] = 1;
for(int i=2;i<=n;i++){
if(!vis[i]){
prime.push_back(i);
g[i] = s[i] = i+1;
}else{
for(auto j:prime[i]){
if(i * j > n){
continue;
}
vis[i*j] = true;
if(i % j == 0){
g[i*j] = g[i]*j+1;
s[i*j] = s[i]/g[i]*g[i*j];
}else{
g[i*j] = j+1;
s[i*j] = s[i]*g[i*j];
}
}
}
}
}
1.8 同余类和剩余系
1.8.1 同余类
对非零整数 \(m\),把全体整数分成 \(|m|\) 个两两不交的集合,且同一个集合中的任意两个数模 \(m\) 均同余,我们把这 \(|m|\) 个集合均称为模 \(m\) 的同余类或剩余类。用 \(r\bmod m\) 表示含有整数 \(r\) 的模 \(m\) 的同余类。
1.8.2 完全剩余系
1.8.3 简化剩余系
2 威尔逊定理
\((p-1)!\equiv \begin {array}{l} \left\{\begin{matrix} -1,p\in P\\ 2,p=4\\ 0,otherwise \end{matrix}\right. \end{array}\pmod p\)
3 欧拉函数
3.1 定义
\(\varphi(n)\) 表示的是小于等于 \(n\) 和 \(n\) 互质的数的个数。
3.2 性质
- 欧拉函数是积性函数,即若 \(gcd(a,b)=1\) 则 \(\varphi(ab)=\varphi(a)\varphi(b)\)。
- 若 \(p\in\mathbb{P}\) 则 \(\varphi(p)=p-1,\varphi(p^k)=p^k-p^{k-1}\)。
- $\varphi(n)=n\times \prod_{i-1}^s \frac{p_i-1}{p_i} $。
3.3 实现
时间复杂度 \(O(\sqrt n)\)。
int solve(int n){
int phi = n;
for(int i=2;i*i<=n;i++){
if(n%i == 0){
phi = phi/i*(i-1);
while(n%i == 0){
n /= i;
}
}
}
if(n != 1){
phi = phi/n*(n-1);
}
return phi;
}
求 \([1,n]\) 的欧拉函数,时间复杂度 \(O(n)\):
int solve(int n){
int
}
3.4 欧拉定理
3.4.1 定义
- \(gcd(a,m)=1 \implies a^{\varphi(m)}\equiv1\pmod m\)。
3.5 扩展欧拉定理
\(a^b= \begin {array}{l} \left\{\begin{matrix} a^b,b<\varphi(m) \\ a^{b\bmod \varphi(m)+\varphi(m)},b\geq\varphi(m) \end{matrix}\right. \end{array}\pmod m\)
4 莫比乌斯函数
4.1 定义
\(\mu(n) \begin {array}{l} \left\{\begin{matrix} -1,n\in \mathbb{P} \\ 0,\exists a_i>1 \\ (-1)^m,otherwise \end{matrix}\right. \end{array}\)
4.2 线性筛法
void solve(int n){
mu[1] = 1;
for(int i=2;i<=n;i++){
if(!vis[i]){
prime.push_back(i);
mu[i] = -1;
}else{
for(auto j:prime){
if(i*j > n){
break;
}
vis[i*j] = true;
if(i%j){
mu[i*j] = 0;
break;
}else{
mu[i*j] = -mu[i];
}
}
}
}
}
5 逆元
5.1 定义
若 \(\exists x,ax\equiv1\pmod b\),则这个正整数 \(x\) 称作 \(a\mod b\) 的逆元。
5.2 求逆元
5.2.1 费马小定理求逆元
费马小定理:\(\forall gcd(a,p)=1\and p\in P,a^{p-1}\equiv1\pmod p,a^p\equiv a\pmod p\)。
\(\because ax\equiv1\pmod b\)
\(\therefore ax\equiv a^{b-1}\pmod b\)。
\(\therefore x\equiv a^{b-2}\mod b\)。
注意费马小定理求逆元,只适用于 \(b\in P\and gcd(a,b)=1\) 的情况下。
5.2.2 欧拉定理求逆元
\(\because ax\equiv1\pmod b\)
\(\therefore ax\equiv a^{\varphi(b)}\pmod b\)。
\(\therefore x\equiv a^{\varphi(b)-1}\mod b\)。
只适用于 \(gcd(a,b)=1\) 的情况。
5.2.3 线性求逆元
inv[1] = 1;
for(int i=2;i<=n;i++){
inv[i] = (long long)(p-p/i)*inv[p%i]%p;
}
5.2.4 线性求任意两个数的逆元
s[0] = 1;
for(int i=1;i<=n;i++){
s[i] = (a[i]*s[i-1]) % p;
}
sn[n] = pow(s[n],p-2,p);
for(int i=n-1;i>0;i--){
sn[i] = (sn[i+1] * a[i+1]) % p;
}
6 扩展欧几里得定理(exgcd)
给定方程 \(ax+by=gcd(x,y)\),求任意一组满足条件的 \(a,b\)。
6.1 代码实现
int exgcd(int a,int b,int %x,int &y){
if(b == 0){
x = 1;
y = 0;
return a;
}
int xx,yy;
int g = exgcd(b,a%b,xx,yy);
x = xx,y = xx-yy*(a/b);
return g;
}
6.2 裴蜀定理
- \(\forall x,y,gcd(a,b)\mid ax+by\);
- \(\exists x,y,ax+by=gcd(a,b)\)。
中国剩余定理(CRT)
定义
中国剩余定理可用于求如下形式的一元线性同余方程。
其中 \(gcd(m_1,m_2,\dots,m_k) =1\)。
求解
-
设 \(M=\prod m_i\)。
-
计算第 \(i\) 个方程的解 \(c_i=\frac{M}{m_i}\)。
-
计算 \(c^{-1}\pmod M\)。
-
\(x=\sum^n_{i=1} r_i,c_i,c_i^{-1}\pmod M\)。
大步小步算法 (BSGS)
定义
大步小步算法(baby-step giant-step,BSGS)通常用于求解 \(a^x\equiv b\pmod p,gcd(a,p) = 1\) 的问题。
时间复杂度 \(O(\sqrt{p})\)。
分析
令 \(x=i\times m-j\),其中 \(m=\lceil p\rceil,i\in [1,m],j\in[0,m-1]\),则 \(a^{i\times m-j}\equiv b\pmod p\),即 \((a^m)^i\equiv b\times a^j\pmod p\)。
所以,求出所有的 \((a^{m})^i\) 和 \(b\times a^j\) 即可。
实现
ll p, a, b;
scanf("%lld%lld%lld", &p, &a, &b);
ll m = ceil(sqrt(p));
ll t = b % p;
hash_table[t] = 0;
for (int i = 1; i <= m; i++) {
t = (ll) t * a % p;
hash_table[t] = i;
}
t = pow_mod(a, m, p);
ll now = 1;
for (int i = 0; i <= m; i++) {
if (hash_table.count(now)) {
printf("%lld\n", i * m - hash_table[now]);
return 0;
}
now = (ll) now * t % p;
}
printf("no solution\n");
组合数学
排列组合
加法原理和乘法原理
完成一件事有 \(N\) 类方法,每类方法有 \(a_i\) 种方法,那么一共有 \(\sum a_i\) 种选法。
完成一件事有 \(N\) 个步骤,每个步骤有 \(a_i\) 种方法,那么一共有 \(\prod a_i\) 种选法。
排列组合基础
排列
从 \(n\) 个元素选 \(m\) 个元素,按一定的顺序排列。
\(P^m_n=\frac{n!}{(n-m)!}\)。
组合
从 \(n\) 个元素选 \(m\) 个元素,不考虑顺序。
\(C^m_n=\frac{n!}{m!(n-m)!}=\frac{p^m_n}{m!}\)。
\(C_n^m=C_n^{n-m}\)。
\(\sum_{i=0}^n C_n^m=2^n\)。
二项式定理
\((a+b)^n=\sum_{i=0}^n C_n^ia^{n-i}b_i\)。
卢卡斯定理
\(C_n^m \mod p=C_{\lfloor n\div p\rfloor}^{\lfloor m\div p\rfloor}\times C_{n\mod p}^{m_\mod p}\mod p,p\in P\)。
代码实现如下:
int c(int n,int m,int p){
if(m > n){
return 0;
}else{
return (ll)f[n]*g[m]*g[n-m]%p;
}
}
int lucas(int n,int m,int p){
if(m == 0){
return 1;
}
return (ll)lucas(n/p,m/p,p) * c(n%p,m%p,p) %p;
}
lucas 函数递归的边界为 \(m=0\)。
注意,要特判 \(m>n\) 的情况。
恒等式
- \(C^{m}_{n+1} = C^m_n\times\frac{n+1}{n-m+1}\)。
- \(\sum^n_1 C^i_n = 2^n\)。
- \(\sum^1_mC^i_n = C_{n+1}^{m+1}\)。
高斯 (Gauss) 消元法
定义
用于求解线性方程:
步骤
一、将系数矩阵消为上三角矩阵
- 枚举主元,找到主元下面系数不是 \(0\) 的一行;
- 交换两行,把这一行与主元交换;
- 将该行同乘一个数,使主元的系数变为 \(1\);
- 把该行的若干被加到其他行,使主元下面的系数变为 \(0\) ;
二、逐个带入求解
此时,我们发现,\(a_n\) 已经知道了,我们将 \(a_n\) 的值带入其他的方程求解即可。
三、无解和无数解的判断
在带入求解完成后,若:
- \(\exist i\in[1,n],\forall j\in[1,n],a_{i,j}=0,a_{i,n+1}\neq0\),则方程无解;
- 在方程有解的前提下 \(\exist i\in[1,n],\forall j\in[1,n+1],a_{i,j}=0\) 则方程有无数解。
实现
时间复杂度 \(O(n^3)\)。
double a[N][N];
const double eps = 1e-6;
bool gauss(int n){
for(int i=1;i<=n;i++){
int j;
for(j=i;j<=n;j++){
if(fabs(a[j][i]) > eps){
break;
}
}
swap(a[i],a[j]);
if(fabs(a[i][i]) < eps){
return 0;
}
for(j = n+1;j>=i;j--){
a[i][j] /= a[i][i];
}
for(j = i+1;j<=n;j++){
for(int k=n+1;k>=i;k--){
a[j][k] -= a[j][i] * a[i][k];
}
}
}
for(int i=n-1;i>=1;i--){
for(int j=i+1;j<=n;j++){
a[i][n+1] -= a[i][j] * a[j][n+1];
}
}
return 1;
}
例题
Luogu-P2455
本题重点考察无解和无穷解的判断。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e2+5;
double a[N][N];
const double eps = 1e-6;
bool gauss(int n){
for(int i=1;i<=n;i++){
int j;
for(j=i;j<=n;j++){
if(fabs(a[j][i]) > eps){
break;
}
}
j = min(j,n);
swap(a[i],a[j]);
if(fabs(a[i][i]) < eps){
continue;
}
for(j = n+1;j>=i;j--){
a[i][j] /= a[i][i];
}
for(j = i+1;j<=n;j++){
for(int k = n+1;k>=i;k--){
a[j][k] -= a[i][k] * a[j][i];
}
}
}
bool flag = true;
bool fflag = false;
for(int i=n;i>=1;i--){
for(int j=i+1;j<=n;j++){
a[i][n+1] -= a[j][n+1] * a[i][j];
a[i][j] = 0;
}
bool flag = true;
for(int j=1;j<=n;j++){
if(fabs(a[i][j]) > eps){
flag = false;
}
}
if(flag == true && fabs(a[i][n+1]) > eps){
puts("-1");
return 0;
}else if(flag == true){
fflag = true;
}
}
if(fflag == true){
puts("0");
return 0;
}else{
return 1;
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n+1;j++){
scanf("%lf",&a[i][j]);
}
}
if(gauss(n)){
for(int i=1;i<=n;i++){
printf("x%d=%.2f\n",i,a[i][n+1]);
}
}
}
ACwing-208
高斯消元解异或方程组。
高斯消元模板为 \(O(n^3)\) 可以通过状态压缩变为 \(O(\frac{n^3}{w})\),对于本题而言,可以优化到 \(O(n^2)\)。
注意位运算的优先级问题!
矩阵
struct node{
int n,m;
int z[105][105];
};
设矩阵 \(a\) 为 \(n\) 行 \(m\) 列。
设矩阵 \(b\) 为 \(x\) 行 \(y\) 列。
单位矩阵和数量矩阵
对于 \(n\times n\) 的矩阵 \(E\),若 \(\forall i\in\left[1,n\right],E_{i,i}=1 \and \forall i,j\in \left[1,n\right],i\neq j,E_{i,j}=0\),则该矩阵 \(E\) 为单位矩阵。
矩阵 \(kE\) 为数量矩阵。
单位矩阵的性质
- \(AE=A,EA=A\)。
矩阵的数乘
\(ans_{i,j}=a_{i,j}*k\)。
矩阵加减法
当且仅当 \(n=x\and m=y\) 时,两个矩阵可以相加减。
\(ans_{i,j}=a_{i,j}+b_{i,j}\)。
\(ans_{i,j}=a_{i,j}-b_{i,j}\)。
矩阵乘法
当且仅当 \(m=x\) 时,两个矩阵可以相乘。
\(ans_{i,j}=\sum a_{i,k}+b_{k,j}\)。
矩阵乘法的性质
-
结合律,即 \(A(BC)=(AB)C\)。
-
左右分配律,即 \(A(B+C)=AB+AB,(B+C)A=BA+CA\)。
-
对数乘的结合性,即 \(\lambda(AB)=A(\lambda B)\)。
-
矩阵满足结合律,当且仅当其中一个矩阵相对于另一个矩阵为单位矩阵、数量矩阵或伴随矩阵。 \(AB=BA \iff B=kE\or B=A^*\)。
代码实现
matrix operator*(const matrix &m1,const matrix &m2){
m3.n = m1.n;
m3.m = m2.m;
for(int i=1;i<=m3.n;i++){
for(int j=1;j<=m3.m;i++){
for(int k=1;k<=m1.m;k++){
m3.z[i][j] += m1.z[i][k]*,m2[k][j];
}
}
}
}
由于内存缓存机制,通常情况下,以 ikj 的顺序枚举的速度相对最快。
应用
斐波那契数列
递推公式
递推式一 O(n):f_n=f_{n-1}+f_{n-2}。
递推式二 O(\log n)f_{2i+1}=f2_{i+1}+f2_{i}。
矩阵乘法
\begin{bmatrix} f_{i-1} & f_{i-2} \ \end{bmatrix}\times \begin{bmatrix} 1 & 1 \1&0\end{bmatrix}=\begin{bmatrix} f_{i} & f_{i-1} \ \end{bmatrix}
矩阵乘法可以配合快速幂使用,时间复杂度为 O(\log n)。
推广
若递推式变为 f_n=f_{n-1}+f_{n-3}。
则矩阵乘法变为:
\begin{bmatrix} f_{i-1} & f_{i-2}&f_{i-3} \ \end{bmatrix}\times \begin{bmatrix} 1 & 1 &0 \0&0&1\1&0&0\end{bmatrix}=\begin{bmatrix} f_{i} & f_{i-1}&f_{i-2} \ \end{bmatrix}

浙公网安备 33010602011771号