数论1(指数和约数).算法
0.算术基本定理
这是基础,先掌握
1.质数
1.1试除法判断质数(O(sqrt(n))
1不是质数, 根据性质,在2 ~sqrt(x)中,若有x可以整除的数则不是质数。
模板
bool is_prime(int x){
if(x == 1) return false;
for(int i = 2; i <= x/i; i ++){
if(x % i == 0) return false;
}
return true;
}
1.2试除法求质因子
题目:给定 n𝑛 个正整数 𝑎𝑖,将每个数分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。
①:x中至多只有一个大于根号n的因数;反证法:若有两个则a1 * a2大于n,不满足因数乘积小于等于n
②:i一定是质数。反证法:假如 i 是一个合数,那么它一定可以分解成多个质因子相乘的形式,这多个质因子同时也是 a 的质因子且比 i 要小,而比 i 小的数*在之前的循环过程中一定是被条件除完了的*
,所以 i 不可能是合数,只可能是质数
③特判大于根号n的质因数
#include <bits/stdc++.h>
using namespace std;
int n, a;
int divide(int x){
for(int i = 2; i <= x/i; i ++){//①
if(x % i == 0){//②
int t = 0;//指数
while(x % i == 0) x = x / i, t ++;//找x里面有几个i
cout << i << " " << t<<endl;
}
}
if(x > 1) cout << x << " "<< 1<<endl;//③
cout << endl;
}
int main(){
cin >> n ;
while(n --){
cin >> a;
divide(a);
}
return 0;
}
1.3筛质数
埃氏筛法
筛掉倍数 例如一组数:2 3 4 5 6 7 8 9 …
4 6 8 9都是前面的倍数,故若p不被筛掉,则2~ p-1中无p的因数,所以p是质数;所以剩下的是质因数
`
int get_prime(){
for(int i = 2; i <= n; i ++){
if(!st[i]){
p[cnt++] = i;//记录质数
for(int j = i; j <= n; j+=i) st[j] = true;//标记筛掉的数
}
}
return cnt;
}
`
2.约数
2.1试除法求约数O(n*根号n)
求x的所有约数,即能被x整除的数;如果i是x的约数,那么n/i也必为x的约数。从而得到了优化。即只要循环遍历1-n/i的数
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.2约数个数
约数个数:若干质因子次方加一的乘积。
思路:用哈希表存储质因子和指数。最后遍历结果。
存储质因子时用上面的试除法。。。
******一个小知识:在int范围内,最多的约数个数大概为1500个
由算术基本定理,每一个数都可以写成若干质因子次方乘积。如下图:
约数个数:若干质因子次方加一的乘积。
证明省略,详见y总–证明
例题:
给定 𝑛 个正整数 𝑎𝑖,请你输出这些数的乘积的约数个数,答案对 1e9+7 取模。
输入格式
第一行包含整数 𝑛。
接下来 𝑛 行,每行包含一个整数 𝑎𝑖。
输出格式
输出一个整数,表示所给正整数的乘积的约数个数,答案需对 10e9+7 取模。
#include <bits/stdc++.h>
using namespace std;
const int M = 1e9 + 7;
typedef long long ll;
int n, x;
int main(){
cin >> n;
unordered_map<int,int> primes;//存放质因数即指数
while(n --){
cin >> x;
for(int i = 2; i <= x/i; i ++){//试除法因式分解
while(x % i == 0){
x /= i;
primes[i]++;
}
}
if(x > 1) primes[x] ++;//特判 最多出现一个大于根号x的质数(前面有证明)
}
ll res = 1;
for(auto prime : primes) res = res * (prime.second + 1) % M;//结论直接用
cout << res;
return 0;
}
2.3约数之和
考虑一个质因数p的e次方的所有约数之和:
那么多个,则为所有可能情况的乘积:
例题:题解
给定 𝑛 个正整数 𝑎𝑖,请你输出这些数的乘积的约数之和,答案对 1e9+7 取模。
输入格式
第一行包含整数 𝑛。
接下来 𝑛 行,每行包含一个整数 𝑎𝑖。
输出格式
输出一个整数,表示所给正整数的乘积的约数个数,答案需对 10e9+7 取模。
关键部分,求各部分的
#include <bits/stdc++.h>
using namespace std;
const int M = 1e9 + 7;
typedef long long ll;
int n, x;
int main(){
cin >> n;
unordered_map<int,int> primes;//存放质因数即指数
while(n --){
cin >> x;
for(int i = 2; i <= x/i; i ++){//试除法因式分解
while(x % i == 0){
x /= i;
primes[i]++;
}
}
if(x > 1) primes[x] ++;//特判 最多出现一个大于根号x的质数(前面有证明)
}
ll res = 1;
for(auto prime : primes){
int p = prime.first, a = prime.second;
ll t = 1;
while( a--){
t = (t*p + 1) %M;//求每一部分的和
}
res = res * t % M;//乘入结果
}
cout << res;
return 0;
}
2.4欧几里得(辗转相除法)求最大公约数
整除与除以的区别:24 ÷ 6 = 4指24能被6整除; 24 除以 6 等于4
内容:较大数除以较小数得到商和余数,再用除数和余数反复做除法运算,当余数为0时,取当前算式除数为最大公约数。
证明:
a, b(a> b)
a = b* q + r(q 为商, r 为余数)
设 d是a, b的一个公约数,那么a = md, b = nd;
带入 md = nd*q + r; r = (m - nq) * d.即d也是r 的一个约数。
所以求a, b的最大公约数即求 b, r的最大公约数
gcd(a,b) = gcd(b, amod b)
法一:公式__gcd(a,b)
c++自带函数1122ms, 比赛时省事就直接用函数吧。。。。。
法二:自写函数gcd(a,b)
1156ms
int gcd(int a, int b){
return b ? gcd(b, a%b):a;
}
r = (m - nq) * d.即d也是r 的一个约数。
所以求a, b的最大公约数即求 b, r的最大公约数
gcd(a,b) = gcd(b, amod b)
法一:公式__gcd(a,b)
c++自带函数1122ms, 比赛时省事就直接用函数吧。。。。。
法二:自写函数gcd(a,b)
1156ms
int gcd(int a, int b){
return b ? gcd(b, a%b):a;
}