赞
踩
都没怎么学过数学呜呜呜呜,太菜了记录一下碰到的各种数学相关的基础知识点吧。
然后其中的ppt部分来自于牛客邓丝雨学姐的算法入门课
费马平方和定理
一个非负整数 c如果能够表示为两个整数的平方和,当且仅当 c 的所有形如 4k + 3 的质因子的幂均为偶数。
赛瓦维斯特定理 :
已知 a,b为大于1的正整数,gcd(a,b)=1,则使不定方程 ax+by=C无负数解的最大整数C=ab-a-b。(小凯的疑惑)
ll ksm(ll a,ll b,ll mod){
ll res = 1;
while(b){
if(b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
N>3时,任意的N 和 N+1 必有一个不是质数。
N和N+2都为质数,这样的一对质数被称为孪生质数。如3和5,5和7,1e9+1和1e9+9
素数筛:
其他的筛法都比较熟了,就只列一个线性筛:
int pri[N+9>>1],now;
bool vis[N+9];
void init(){
for(int i=2;i<=N;i++){
if(!vis[i])pri[++now]=i;
for(int j=1;j<=now&&pri[j]*i<=N;j++){
vis[pri[j]*i]=1;
if(i%pri[j]==0)break;
}
}
}
求最大公因数的递归写法辗转相除法
ll gcd(ll a, ll b){
if(b == 0) return a;
else return gcd(b, a%b);
}
最小公倍数 l c m = a / g c d ( a , b ) ∗ b lcm=a/gcd(a,b)*b lcm=a/gcd(a,b)∗b
还有几个公式
g
c
d
(
k
a
,
k
b
)
=
k
∗
g
c
d
(
a
,
b
)
gcd(ka,kb)=k*gcd(a,b)
gcd(ka,kb)=k∗gcd(a,b)
l
c
m
(
k
a
,
k
b
)
=
k
∗
l
c
m
(
a
,
b
)
lcm(ka,kb)=k*lcm(a,b)
lcm(ka,kb)=k∗lcm(a,b)
l
c
m
(
S
a
,
S
b
)
=
S
g
c
d
(
a
,
b
)
lcm(\frac{S}{a},\frac{S}{b})=\frac{S}{gcd(a,b)}
lcm(aS,bS)=gcd(a,b)S
贝祖定理:即如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。
换句话说,如果ax+by=m有解,那么m一定是gcd(a,b)的若干倍。(可以来判断一个这样的式子有没有解)
有一个直接的应用就是 如果ax+by=1有解,那么gcd(a,b)=1
int exgcd(int a,int b,int &x,int &y)//扩展欧几里得算法
{
if(!b)
{
x=1;y=0;
return a; //到达递归边界开始向上一层返回
}
int r=exgcd(b,a%b,x,y);
int temp=y; //把x y变成上一层的
y=x-(a/b)*y;
x=temp;
return r; //得到a b的最大公因数
}
欧拉函数是求小于等于n的数中与n互质的数的数目
2020年省赛出过的点,当时就是没学过欧拉函数卡在这儿了
计算公式:
欧拉函数是一个积性函数:
ϕ
(
a
×
b
)
=
ϕ
(
a
)
×
ϕ
(
b
)
\phi(a\times b)=\phi(a) \times\phi(b)
ϕ(a×b)=ϕ(a)×ϕ(b)
首先上证明(参考博客:https://www.cnblogs.com/linyujun/p/5194170.html)
随便这个大佬的博客tql,建议反复看
证明过程需要引入一个唯一分解定理:
(2020的蓝桥杯国赛的一个填空题出过,我不知道当时我怎么就忘了,太菜了)
唯一分解定理又称为算数基本定理,基本内容是:
每个大于1的自然数,要么本身就是质数,要么可以写为2个或以上的质数的积,而且这些质因子按大小排列之后,写法仅有一种方式。
用另一种方法表示就是:对于任何一个大于1的正整数,都存在一个标准的分解式: N = p 1 a 1 ∗ p 2 a 2 ∗ ⋅ ⋅ ⋅ ∗ p n a n N=p_1^{a_1} * {p_2}^{a_2}*···*p_n^{a_n} N=p1a1∗p2a2∗⋅⋅⋅∗pnan(其中一系列 a n a_n an为指数, p n p_n pn为质数)
此定理表明:任何一个大于 1 的正整数都可以表示为素数的积。
通过唯一分解定理,可以先求出一个数x的质因数,然后删掉所有小于x的质因数的倍数的个数,重复减去的又要加上(容斥嘛),然后用一种简单写法写容斥就可以了。
欧拉函数的代码(其中包括了怎么分解质因数):
int phi(int x){
int ans = x;
for(int i = 2; i*i <= x; i++){
if(x % i == 0){
ans = ans / i * (i-1);
while(x % i == 0) x /= i;//分解质因数
}
}
if(x > 1) ans = ans / x * (x-1);//除剩下的那个数如果不是1也是质因数
return ans;
}
另外唯一分解定理还有推论:
N
=
p
1
a
1
∗
p
2
a
2
∗
⋅
⋅
⋅
∗
p
n
a
n
N=p_1^{a_1} * {p_2}^{a_2}*···*p_n^{a_n}
N=p1a1∗p2a2∗⋅⋅⋅∗pnan
约数个数 ( a 1 + 1 ) ( a 2 + 1 ) … … ( a n + 1 ) (a_1+1)(a_2+1)……(a_n +1) (a1+1)(a2+1)……(an+1)
约数之和 ( 1 + p 1 1 ∗ + p 1 2 + ⋅ ⋅ ⋅ + p 1 a 1 ) ∗ ⋅ ⋅ ⋅ ∗ ( 1 + p k 1 ∗ + p k 2 + ⋅ ⋅ ⋅ + p k a k ) (1+{p_1}^{1} *+{p_1}^{2}+···+{p_1}^{a_1})*···*(1+{p_k}^{1} *+{p_k}^{2}+···+{p_k}^{a_k}) (1+p11∗+p12+⋅⋅⋅+p1a1)∗⋅⋅⋅∗(1+pk1∗+pk2+⋅⋅⋅+pkak)
如果正整数n和a互质,那么就有 a ϕ ( n ) ≡ 1 ( m o d n ) a^{\phi(n)}\equiv1(\mod n) aϕ(n)≡1(modn),其中 ϕ ( n ) \phi(n) ϕ(n)是欧拉函数
推论:欧拉降幂
当指数很大时,可以用欧拉定理降幂:
如果n和a互质,那么
a
b
≡
a
b
m
o
d
ϕ
(
n
)
(
m
o
d
n
)
a^b \equiv a^{b\mod \phi(n)}(\mod n)
ab≡abmodϕ(n)(modn)
或者说,若p是质数,且a,p互质,那么 a的(p-1)次方除以p的余数恒等于1。
可以用来求逆元,另外欧拉定理是费马小定理的推广。
在有取模的情况下,除法需要特殊处理,也就是逆元,将除法变为乘法。(关于逆元https://blog.csdn.net/u011815404/article/details/81298032可以看这个大佬的博客)
中国剩余定理用于求解一元线性同余方程组
比如说:一堆物品,3个3个分剩2个,5个5个分剩3个,7个7个分剩2个。
用inv(x,y)表示x在模y下的逆元
用中国剩余定理其实就相当于构造 2 × ( 5 × 7 × i n v ( 5 × 7 , 3 ) ) 2\times(5\times7\times inv(5\times7,3)) 2×(5×7×inv(5×7,3)), 3 × ( 3 × 7 × i n v ( 3 × 7 , 5 ) ) 3\times(3\times7\times inv(3\times7,5)) 3×(3×7×inv(3×7,5)), 2 × ( 3 × 5 × i n v ( 3 × 5 , 7 ) ) 2\times(3\times5\times inv(3\times5,7)) 2×(3×5×inv(3×5,7)),最终的答案是这三个数的和。
代码:
//中国剩余定理模板 typedef long long ll; ll china(ll a[],ll b[],int n)//a[]为除数,b[]为余数 { ll M=1,y,x=0; for(int i=0;i<n;++i) //算出它们累乘的结果 M*=a[i]; for(int i=0;i<n;++i) { ll w=M/a[i]; ll tx=0; int t=exgcd(w,a[i],tx,y); //计算逆元 x=(x+w*(b[i]/t)*x)%M; } return (x+M)%M; }
奇数个集合相加,偶数个集合相减
组合公式: C n m = n ! m ! ( n − m ) ! C_n^m = \frac{n!}{m!(n - m)!} Cnm=m!(n−m)!n!
基本性质:
递推关系: C n m = C n − 1 m + C n − 1 m − 1 C_n^m = C_{n - 1}^{m} + C_{n - 1}^{ m - 1} Cnm=Cn−1m+Cn−1m−1(可以用杨辉三角辅助记忆)
(背住,求组合数小的时候用这个公式递推,大了就用卢卡斯)
一般来说,求组合数 C n m C_n^m Cnm时,n<1000,用递推公式;n<1e5,用逆元;n<1e18,且p为质数,用lucas。
你直接算出来一个组合数的结果直接对p取模,结果一定是对的,但是n大了之后答案存不下太大了,所以需要取模。
逆元求就是我们现实中的公式法,不同的是变除法为乘除数的逆元。
求逆元的两种方法:
1、费马小定理(有限制,p要为素数)
p为素数时,a关于mod p的逆元为a^(p-2)mod p。用快速幂模。
但是实际上在acm中题目给的p基本都是质数,所以这是最好用的啦。
2、扩展欧几里得(普遍使用的求逆元方法)
例题:组合数问题https://ac.nowcoder.com/acm/contest/9986/F
对应题解可以看我这篇博客F题
Lucas定理是用来求 C n m C_n^m Cnmmod p,p为素数的值。(p最大1e5左右)。
是由递推式得到的。
这个可以参考我的博客中:B题
代码:
ll lucas(ll n,ll m){
if(m==0)
return 1;
return (C(n%p,m%p)*lucas(n/p,m/p))%p;
}
从有n个不同元素的集合任取r个元素的排列方式有:
P
(
n
,
r
)
=
n
∗
(
n
−
1
)
∗
.
.
.
∗
(
n
−
r
+
1
)
=
n
!
(
n
−
r
)
!
P(n, r) = n*(n-1)*...*(n-r+1) = \frac{n!} { (n-r)!}
P(n,r)=n∗(n−1)∗...∗(n−r+1)=(n−r)!n!
特别地
P
(
n
,
n
)
=
n
!
P(n,n) = n!
P(n,n)=n!
或者
M
=
{
k
1
⋅
a
1
,
k
2
⋅
a
2
,
⋯
,
k
n
⋅
a
n
}
M=\{k_1⋅a_1,k_2⋅a2,⋯,k_n⋅a_n\}
M={k1⋅a1,k2⋅a2,⋯,kn⋅an}
(其中每个
a
i
a_i
ai代表是不同的元素,每个元素
a
i
a_i
ai有
k
i
k_i
ki个,
k
i
k_i
ki可以是有限数,也可以是∞。)
多重集合 M = k 1 ⋅ a 1 , k 2 ⋅ a 2 , ⋯ , k n ⋅ a n M={k_1⋅a_1,k_2⋅a2,⋯,k_n⋅a_n} M=k1⋅a1,k2⋅a2,⋯,kn⋅an的 r r r排列数为 k r kr kr
多重集合 M = { k 1 ⋅ a 1 , k 2 ⋅ a 2 , ⋯ , k n ⋅ a n } M =\{k_1⋅a_1,k_2⋅a2,⋯,k_n⋅a_n\} M={k1⋅a1,k2⋅a2,⋯,kn⋅an}的全排列数为: ( k 1 + k 2 + ⋯ + k n ) ! k 1 ! k 2 ! ⋯ k n ! \frac{(k1+k2+⋯+kn)!}{k1!k2!⋯kn!} k1!k2!⋯kn!(k1+k2+⋯+kn)!
高斯消元就是线代中学过,我们对线性方程组可以做如下的三种变换:
(1)互换行列变换
(2)倍乘行列变换
(3)倍加行列变换
经过这种初等变换的线性方程组与原来的线性方程组是同解,高斯消元法求解线性方程组就是利用初等行变换将线性方程组的增广矩阵化为阶梯型矩阵。
矩阵的秩R:最后化得的阶梯型矩阵中非0行的行数
自由元:0行的行数
无解:消元完后,发现一行系数为0,但是常数不为0
多解:因为最后全化为0了,这些变量就可以随意取值都有解。所以自由元有多少就是有几个多解。
然后是代码,来自于kuangbin的模板:
#include<stdio.h> #include<algorithm> #include<iostream> #include<string.h> #include<math.h> using namespace std; const int MAXN=50; int a[MAXN][MAXN];//增广矩阵 int x[MAXN];//解集 bool free_x[MAXN];//标记是否是不确定的变元 int gcd(int a,int b){ if(b == 0) return a; else return gcd(b,a%b); } inline int lcm(int a,int b){ return a/gcd(a,b)*b;//先除后乘防溢出 } // 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解, //-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数) //有equ个方程,var个变元。增广矩阵行数为equ,分别为0到equ-1,列数为var+1,分别为0到var. int Gauss(int equ,int var){ int i,j,k; int max_r;// 当前这列绝对值最大的行. int col;//当前处理的列 int ta,tb; int LCM; int temp; int free_x_num; int free_index; for(int i=0;i<=var;i++){ x[i]=0; free_x[i]=true; } //转换为阶梯阵. col=0; // 当前处理的列 for(k = 0;k < equ && col < var;k++,col++){// 枚举当前处理的行. // 找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差) max_r=k; for(i=k+1;i<equ;i++){ if(abs(a[i][col])>abs(a[max_r][col])) max_r=i; } if(max_r!=k){// 与第k行交换. for(j=k;j<var+1;j++) swap(a[k][j],a[max_r][j]); } if(a[k][col]==0){// 说明该col列第k行以下全是0了,则处理当前行的下一列. k--; continue; } for(i=k+1;i<equ;i++){// 枚举要删去的行. if(a[i][col]!=0){ LCM = lcm(abs(a[i][col]),abs(a[k][col])); ta = LCM/abs(a[i][col]); tb = LCM/abs(a[k][col]); if(a[i][col]*a[k][col]<0)tb=-tb;//异号的情况是相加 for(j=col;j<var+1;j++){ a[i][j] = a[i][j]*ta-a[k][j]*tb; } } } } // 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0). for (i = k; i < equ; i++){ // 对于无穷解来说,如果要判断哪些是自由变元,那么初等行变换中的交换就会影响,则要记录交换. if (a[i][col] != 0) return -1; } // 2. 无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵. // 且出现的行数即为自由变元的个数. if (k < var){ return var - k; // 自由变元有var - k个. } // 3. 唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵. // 计算出Xn-1, Xn-2 ... X0. for (i = var - 1; i >= 0; i--){ temp = a[i][var]; for (j = i + 1; j < var; j++){ if (a[i][j] != 0) temp -= a[i][j] * x[j]; } if (temp % a[i][i] != 0) return -2; // 说明有浮点数解,但无整数解. x[i] = temp / a[i][i]; } return 0; } int main(void){ // freopen("in.txt", "r", stdin); // freopen("out.txt","w",stdout); int i, j; int equ,var; while (scanf("%d %d", &equ, &var) != EOF){ memset(a, 0, sizeof(a)); for (i = 0; i < equ; i++){ for (j = 0; j < var + 1; j++){ scanf("%d", &a[i][j]); } } int free_num = Gauss(equ,var); if (free_num == -1) printf("无解!\n"); else if (free_num == -2) printf("有浮点数解,无整数解!\n"); else if (free_num > 0){ printf("无穷多解! 自由变元个数为%d\n", free_num); for (i = 0; i < var; i++){ if (free_x[i]) printf("x%d 是不确定的\n", i + 1); else printf("x%d: %d\n", i + 1, x[i]); } }else{ for (i = 0; i < var; i++){ printf("x%d: %d\n", i + 1, x[i]); } } printf("\n"); } return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。