第四章 数学知识(一)
一、质数
1.质数的判定——试除法
时间复杂度 O ( n ) O(\sqrt n) O(n)
bool is_prime(int n){
if(n<2) return false;
for(int i=2;i<=n/i;i++)
if(n%i==0) return false;
return true;
}
2.质因数分解——试除法
时间复杂度 O ( log n ) O(\log n) O(logn)~ O ( n ) O(\sqrt n) O(n)
void divide(int x){
for(int i=2;i<=n/i;i++)
if(n%i==0){
int s=0;
while(n%i==0){
n/=i;
s++;
}
printf("%d %d\n",i,s);
}
if(n>1) printf("%d %d\n",n,1);
}
3.筛质数
(1)Eratosthenes筛法
基本思想:任意整数 x x x的倍数都不是质数
优点:可以遍历每个质因数
朴素筛法
时间复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn)
int primes[N],cnt;
bool st[N];
void get_primes(int n){
for(int i=1;i<=n;i++){
if(!st[i]) primes[cnt++]=i;
for(int j=2*i;j<=n;j+=i)
st[j]=true;
}
}
优化:对于每个数
x
x
x,只需要从
x
2
x^2
x2开始标记
时间复杂度
O
(
n
log
log
n
)
O(n\log \log n)
O(nloglogn)
int primes[N],cnt;
bool st[N];
void get_primes(int n){
for(int i=2;i<=n;i++){
if(!st[i]){
primes[cnt++]=i;
for(int j=i;j<=n/i;j++)
st[j*i]=true;
}
}
}
(2)线性筛法
基本思想:每个合数只会被它的最小质因子筛掉
时间复杂度
O
(
n
)
O(n)
O(n)
int primes[N],cnt;
bool st[N];
void get_primes(int n){
for(int i=2;i<=n;i++){
if(!st[i]) primes[cnt++]=i;
for(int j=0;primes[j]<=n/i;j++){
st[primes[j]*i]=true;
if(i%primes[j]==0) break;
}
}
}
线性筛可以筛一切积性函数
二、约数
1.在
1
0
9
10^9
109以内,一个数的因数最多只有
1344
1344
1344个,当遇到需要因数的题目时,可以考虑枚举
2.前
16
16
16个质数乘积
>
1
0
18
>10^{18}
>1018
1.试除法求所有约数
vector<int> get_divisors(int x){
vector<int> res;
for(int i=1;i<=x/i;i++)
if(x%i==0){
res.push_back(i);
if(i!=x/i) res.push_back(x/i);
}
sort(res.begin(),res.end());
return res;
}
2.约数个数与约数之和
将N分解质因数得:
N
=
∏
i
=
1
k
a
i
p
i
N=\prod^k_{i=1}{a_i}^{p_i}
N=i=1∏kaipi
则约数个数为:
∏
i
=
1
k
(
p
i
+
1
)
\prod_{i=1}^k(p_i+1)
i=1∏k(pi+1)
约数之和为:
∏
i
=
1
k
∑
j
=
0
p
i
a
i
j
\prod_{i=1}^k\sum_{j=0}^{p_i}{a_i}^j
i=1∏kj=0∑piaij
3.欧几里得算法求最大公约数
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
三、欧拉函数
欧拉函数定义:1~N中与N互质的数的个数,记作 ϕ ( N ) \phi(N) ϕ(N)
欧拉函数是积性函数
1.公式求欧拉函数
将N分解质因数得:
N
=
∏
i
=
1
k
a
i
p
i
N=\prod^k_{i=1}{a_i}^{p_i}
N=i=1∏kaipi
则有:
ϕ
(
N
)
=
N
∗
∏
i
=
1
k
(
1
−
1
p
i
)
\phi(N)=N*\prod_{i=1}^k({1-\frac{1}{p_i}})
ϕ(N)=N∗i=1∏k(1−pi1)
时间复杂度
O
(
n
)
O(\sqrt n)
O(n)
int euler(int a){
int res=a;
for(int i=2;i<=a/i;i++){
if(a%i==0){
while(a%i==0) a/=i;//分解质因数
res=res/i*(i-1);
}
}
if(a>1) res=res/a*(a-1);
return res;
}
2.筛法求欧拉函数
原理:在线性筛质数时进行推导
时间复杂度
O
(
n
)
O(n)
O(n)
int primes[N];//存储质数
int phi[N];//存储欧拉函数
bool st[N];//存储是否被筛掉
int cnt;
void get_eulers(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!st[i]){
primes[cnt++]=i;
phi[i]=i-1;
}
for(int j=0;primes[j]<=n/i;j++){
int t=primes[j]*i;
st[t]=true;
if(i%primes[j]==0){
phi[t]=phi[i]*primes[j];
break;
}
phi[t]=phi[i]*(primes[j]-1);
}
}
}
3.欧拉定理
欧拉定理:
若 a 与 n 互 质 , 则 a ϕ ( n ) = 1 m o d n 若a与n互质,则a^{\phi(n)}=1 \mod n 若a与n互质,则aϕ(n)=1modn
费马小定理(欧拉定理推论):
若 a 与 n 互 质 , 且 n 为 质 数 , 则 a n − 1 = 1 m o d n 若a与n互质,且n为质数,则a^{n-1}=1\mod n 若a与n互质,且n为质数,则an−1=1modn
四、快速幂
a
k
=
a
2
x
1
+
2
x
2
+
⋯
+
2
x
k
=
a
2
x
1
∗
a
2
x
2
∗
⋯
∗
a
2
x
k
a^k=a^{2^{x_1}+2^{x_2}+\cdots+2^{x_k}}=a^{2^{x_1}}*a^{2^{x_2}}*\cdots*a^{2^{x_k}}
ak=a2x1+2x2+⋯+2xk=a2x1∗a2x2∗⋯∗a2xk
于是可以将问题转化为求 k 的二进制表示
时间复杂度从普通幂运算
O
(
k
)
O(k)
O(k)降到
O
(
log
k
)
O(\log k)
O(logk)
//求a^k%p
int qpow(int a,int k,int p){
int res=1%p,t=a;
while(k){
if(k&1) res=res*t%p;
t=t*t%p;
k>>=1;
}
return res;
}
五、扩展欧几里得算法
1.裴蜀定理
对于任意正整数
a
,
b
a,b
a,b,一定存在整数
x
,
y
x,y
x,y,使得
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b)成立
重要推论: 对于任意正整数
a
,
b
a,b
a,b,若存在整数
x
,
y
x,y
x,y,使得
a
x
+
b
y
=
1
ax+by=1
ax+by=1,则
a
,
b
a,b
a,b互质
2.扩展欧几里得算法
求 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)的一组解
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return d;
}