【ACWING】数论1
1 质数
1.1 质数的判定 -- 试除法O(n)
bool is_prime(int n) { if(n < 2) return false; //for(int i = 2; i < n; i++) // 这个最简单,但是复杂度高 for(int i = 2; i <= n / i; i ++) // 实际上只枚举了sqrt(n)次 if(n%i == 0) return false; return true; }
在上述优化中,推荐写成i<=n/i
,不推荐i * i < n
,因为i*i
会爆int,也不推荐sqrt(n)
,因为每次循环都会算一次
1.2 分解质因数----试除法
首先由算术基本定理可知,任何整数都能分解成若干个质因子的乘积。
void divide(int n) { for(int i = 2; i <= n; i++) // 从2开始枚举,2是第一个质数 if(n%i ==0) //发现能整除 i { int s = 0; // 统计除了几次i while(n%i==0) // 只要还能除i,就继续除 { n/=i; s++; } printf("%d %d\n",i,s); } } // 下面这个写法好像更好 void divide(int n) { unordered_map<int,int> primes; for(int i = 2; i <= n/i; i++){ while(n%i==0) { n/=i; primes[i]++; } } if(n>1) primes[n]++; // 剩余一个大于n/i的质因子 }
优化1---O(sqrt(n))
n中最多只包含一个大于sqrt(n)的质数
void divide(int n) { for(int i = 2; i <= n / i; i++) // 只枚举到sqrt(n) if(n%i ==0) //发现能整除 i { int s = 0; // 统计除了几次i while(n%i==0) // 只要还能除i,就继续除 { n/=i; s++; } printf("%d %d\n",i,s); } if(n>1) printf("%d %d\n",n,1); // 最后仅剩的大于sqrt(n)的质因子 }
1.3 质数筛
最朴素版本
void get_primes(int n) { for(int i = 2; i <= n; i++) // 从2开始判断 { if(!st[i]) { primes[cnt++] = i; // 如果i这个数没有被筛掉,说明i是质数,primes存的是质数表 } for(int j = i + i; j <= n; j += i) st[j] = true; } }
埃式筛
void get_primes(int n) { for(int i = 2; i <= n; i++) // 从2开始判断 { if(!st[i]) { primes[cnt++] = i; // 如果i这个数没有被筛掉,说明i是质数,primes存的是质数表 for(int j = i + i; j <= n; j += i) st[j] = true; // 把这个放进循环里面了,相当于只筛质数 } } }
欧拉筛
void get_primes(int n) { for(int i = 2; i <= n; i++) // 对2-n这些数一个个处理 { if(!st[i]) primes[cnt++] = i; // st[] = false,表示这个数没有被筛掉 for(int j = 0; primes[j] <= n/i; j++) // 用[质数*i]得到的数一定不是质数,筛掉(保证这个数<n) { st[primes[j] * i] = true; // if(i % primes[j] == 0) break; } } }
2 约数
2.1 试除法求约数
因为约数是成对出现的,假设n能被d整除,那么n同样能够被n/d整除,所以枚举的时候有d<=n/d,所以d<=sqrt(n)
vector<int> get_divisors(int n) { vector<int> res; for(int i = 1; i <= n/i; i ++) { if(n%i==0) // 能整除,说明i是n的约数 { // 约数成对出现,加入数组 res.push_back(i); if(i!= n/i) res.push_back(n/i); } } sort(res.begin(),res.end()); return res; }
2.2 约数个数 约数之和
由算术基本定理来推
2.3 欧几里得算法/辗转相除法法
原理:gcd(a,b) = gcd(b,a%b)
辗转相除
int gcd(int a, int b) { return b ? gcd(b,a % b) : a; }
同时可知,最小公倍数lcm = a*b / gcd(a,b)
分类:
算法 / ACWING算法基础课
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步