洛谷原文:GESP五级知识点汇总
任何遗漏、错误、疑问、建议,请在评论区指出哦~
数论部分
线性筛
线性筛,又称欧拉筛。其时间复杂度为 O(n) ,是一种线性时间复杂度的算法,并因此而得名。
线性筛可以筛出区间 [2,n] 内的所有质数。
核心思想
每个合数有且仅有一次被其最小的质因数标记,从而避免重复操作,达到线性复杂度。
数据结构
1. isPrime[]
一个 bool 类型数组,长度为 n+1 。若 isPrime[i] 的值为 true ,则代表 i 是质数;反之, i 是合数。初始化时所有元素的值都为 true 。
2. primes[]
一般用 vector 存储(即一个动态数组),或者用一个足够大的静态数组。
这个数组内存储已经找到的质数。
算法步骤
1.初始化: 将 isPrime 内的所有元素的值都设置为 true 。(不用把 isPrime[0] 和 isPrime[1] 设置为 false ,因为 0 和 1 都不是质数。但为了严谨性,设置了也未尝不可)
2.遍历每个数 i(从2到n):
-
若
isPrime[i]==true,则将i丢进primes里。 -
用已找到的素数
primes[j]与当前数字i相乘,标记i*primes[j]为合数,即isPrime[i*primes[j]]=false。 -
若
i能被primes[j]整除,停止内层循环。因为后续i与更大素数的乘积,已在之前被筛过。例如i=4,primes[j]=2时,若不停止,4∗3 会在之后被 6∗2 重复筛除。 -
遍历结束后,
primes内存储的值就是区间 [2,n] 内的所有质数。
代码示例
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN=1e6+7;
bool isPrime[MAXN];
vector<int> primes;
void get(int n) {
for(int i=2;i<=n;i++)
isPrime[i]=true;
for(int i=2;i<=n;i++) {
if(isPrime[i])
primes.push_back(i);
for(int j=0;j<primes.size()&&i*primes[j]<=n;j++) {
isPrime[i*primes[j]]=false;
if(i%primes[j]==0)
break;
}
}
}
int main() {
int x;
cin>>x;
get(x);
for(int i=0;i<primes.size();i++)
cout<<primes[i]<<" ";
return 0;
}
欧几里得算法(辗转相除法)
欧几里得算法是一个求最大公因数(GCD)的算法。
欧几里得算法,又称“辗转相除法”,(“辗”音 zhan ,三声)。
核心思想
两个整数的最大公约数等于其中较小的数和两数相除余数的最大公约数。
即:(a>b)
gcd(a,b)=gcd(b,amodb)
注释:amodb ,a 除以 b 的余数,即 a%b。
显而易见地,可以用递归实现。
算法证明
步骤 1:设定、符号定义
- 设 d=gcd(a,b),即 d 是 a 和 b 的最大公约数
- 设 r=amodb,根据带余除法,存在整数 q 和 r(0≤r<b)使得:a=b⋅q+r
- 需要证明:gcd(a,b)=gcd(b,r)
步骤 2:证明 d 是 b 和 r 的公约数
- 由 d=gcd(a,b),得 d∣a 且 d∣b
- 由 a=bq+r 可得:r=a−bq
- 因为 d∣a 且 d∣b,所以 d∣(a−bq)
- 即 d∣r,故 d 是 b 和 r 的公约数
步骤 3:证明 d 是最大公约数(反证法)
- 假设存在 d′=gcd(b,r) 且 d′>d
- 由 d′=gcd(b,r),得 d′∣b 且 d′∣r
- 由 a=bq+r,得 d′∣(bq+r),即 d′∣a
- 于是 d′∣a 且 d′∣b,即 d′ 是 a 和 b 的公约数
- 但 d=gcd(a,b) 是最大公约数,d′>d 与之矛盾
- 故假设不成立,d 就是 b 和 r 的最大公约数
步骤 4:算法终止性证明
- 算法迭代过程:gcd(a,b)=gcd(b,r0)=gcd(r0,r1)=⋯
- 余数序列严格递减:b>r0>r1>r2>⋯≥0
- 根据良序原理,余数必然在有限步内达到 0
- 当 rk=0 时:gcd(rk−1,0)=rk−1
- 此时的 rk−1 即为所求的最大公约数
代码示例
1.递归版本
#include<iostream>
using namespace std;
int gcd1(int a,int b) {
if(b==0)
return a;
return gcd1(b,a%b);
}
int main() {
int a,b;
cin>>a>>b;
cout<<gcd1(a,b)<<endl;
return 0;
}
2.迭代版本
#include<iostream>
using namespace std;
int gcd2(int a,int b) {
while(b) {
int temp=b;
b=a%b;
a=temp;
}
return a;
}
int main() {
int a,b;

&spm=1001.2101.3001.5002&articleId=156108315&d=1&t=3&u=58d920f5b7e74516921be13982917ee1)
2184

被折叠的 条评论
为什么被折叠?



