文章目录
- [约数 / 因子](#约数 / 因子)
- [试除法求约数: O ( n ) O(\sqrt{n}) O(n )](#试除法求约数: O ( n ) O(\sqrt{n}) O(n ))
- 约数的数量与约数之和
- [最大公约数: O ( log max ( a , b ) ) O(\log{\max{(a,b)}}) O(logmax(a,b))](#最大公约数: O ( log max ( a , b ) ) O(\log{\max{(a,b)}}) O(logmax(a,b)))
约数 / 因子
数学定义
- 在数论中,约数等于因子。
- 例如对于12来说,约数有1,2,3,4,6,12。
- 对于一个数x,任何能整除x的数都是x的约数。 a ∈ [ 1 , x ] , a ∣ x a\in [1,x], a|x a∈[1,x],a∣x
试除法求约数: O ( n ) O(\sqrt{n}) O(n )
数学原理
- 约数的对称性:假设i是x的约数之一,那么x/i(注意可能与i相等)一定也是x的约数。
- 如果一个数 n n n 是合数,它一定有一个小于等于 n \sqrt{n} n 的因子。
- 注意特判:i从1开始保证对于素数也成立。
实现
cpp
void get_divisors(int x) {
set<int>res;
for (int i = 1; i <= x / i; i++) {
if (x % i == 0) {
res.insert(i);
res.insert(x / i);
}
}
for (auto item : res) {
cout << item << " ";
}
cout << endl;
}
约数的数量与约数之和
数学原理
算术基本定理(Fundamental Theorem of Arithmetic)指出:
每个大于 1 的正整数 n n n 都可以唯一地分解成有限个素数的乘积,即
n = p 1 a 1 p 2 a 2 ⋯ p k a k n = p_1^{a_1} p_2^{a_2} \cdots p_k^{a_k} n=p1a1p2a2⋯pkak
其中 p 1 < p 2 < ⋯ < p k p_1 < p_2 < \cdots < p_k p1<p2<⋯<pk 是互不相同的素数, a 1 , a 2 , ... , a k a_1, a_2, \dots, a_k a1,a2,...,ak 是正整数。若忽略素因数的排列顺序,该分解是唯一的。
约数的数量: O ( log n ) − O ( n ) O(\log n)-O(\sqrt{n}) O(logn)−O(n )

- 简单理解:我们有的是素因子,但是我们可以通过组合素因子的方法得到合数因子!
- 注意:指数可以取0。
实现
- 使用map存储一下素因子即可
cpp
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
int a;
cin >> a;
for (int j = 2; j <= a / j; j++) {
while (a % j == 0) {
mp[j]++;
a /= j;
}
}
if (a > 1)mp[a]++;
//for (auto item : mp) {
// cout << item.first << " " << item.second << endl;
//}
//cout << endl;
}
ll res = 1;
for (auto item : mp) {
res *= (ll)(item.second + 1);
res %= mod;
}
cout << res;
}
约数之和: O ( log n ) − O ( n ) O(\log n)-O(\sqrt{n}) O(logn)−O(n )

- 简单理解:既然我们知道任何一个因子都可以由素因子组合得到 。因此我们对所有因子进行求和,可以化简为对单个因子进行求和,最后乘积的形式。
实现
- 对单个因子求和的公式如下: S n = S n − 1 ∗ p + 1 S_n=S_{n-1}*p+1 Sn=Sn−1∗p+1,循环n次即可。
- 最后分别乘在一起即可。
cpp
int n;
unordered_map<int, int>mp;
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
int a;
cin >> a;
for (int j = 2; j <= a / j; j++) {
while (a % j == 0) {
mp[j]++;
a /= j;
}
}
if (a > 1)mp[a]++;
}
//for (auto item : mp) {
// cout << item.first << " " << item.second << endl;
//}
//cout << endl;
ll res = 1;
for (auto item : mp) {
ll sum = 1;//S0
int p = item.first;
for (int i = 1; i <= item.second; i++) {
sum = (ll)(sum * p) + 1;
sum %= mod;
}
//cout << sum << endl;
res = (res * sum) % mod;
}
cout << res;
}
最大公约数: O ( log max ( a , b ) ) O(\log{\max{(a,b)}}) O(logmax(a,b))
数学原理
- g c d ( a , b ) = g c d ( b , r ) gcd(a,b)=gcd(b,r) gcd(a,b)=gcd(b,r),where a = b q + r a=bq+r a=bq+r
- 证明思路:a,b,r的公约数都是d。
- 假设d是a和b的公约数,那么证明d也是r和b的公约数;假设d是b和r的公约数,那么d也是a的公约数。使用整除的线性性质即可 ,
d|a,d|b -> d|ax+by
实现
- 注意gcd(a,0)定义为a。a一定要大于b!a一定要大于b!a一定要大于b!。
cpp
int gcd(int x, int y) {// x>y
if (y == 0)return x;
else {
return gcd(y, x % y);
}
}