约数之和 约数个数 最大公约数
约数公式
关于约数,有两个公式,一个是约数个数公式,一个是约数之和公式
约数个数
求\(n\)个数相乘的因数个数,答案对 \(10^9+7\) 取模
公式
\[\begin{align} N = &p_1^{n_1} * p_2^{n_2} *\dots * p_k^{n_k}\\ 因数个数 = &(n_1+1)(n_2+1)\dots(n_k+1)\\ &因为 任何一个 约数 d可以表示成\\ d=&p_1^{m_1} * p_2^{m_2} * \dots * p_k^{m_k},0\leq m_i\leq m_i
\end{align}
\]
想法
小学奥数题,指数加一再连乘
把一个数分解质因数,再把每一个质因数的指数加一乘起来
比如一个数\(N\)分解成\(P_1^{n_1} \times P_2^{n_2} \times \dots\times P_k^{n_k}\) 通过组合来理解
如果两项的\(n_i\)不同,那么就会产生不同的因数
所以\(N\)的因数个数和指数\(n_i\)的选法个数相等
对于\(P_i\)的指数\(n_i\),可以选择\(0,1,2,3 \dots i\)个,也就是\(i+1\)种选法
所以每个\(P_i\)的选法\(i+1\)乘起来就是总的选法,也就是\(N\)的因数个数
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long LL;
const int mod = 1e9+7;
int main()
{
int n;
scanf("%d", &n);
unordered_map<int, int> primes; // 用哈希表存,数组存不下
while (n -- )
{
int x;
scanf("%d", &x);
// 分解质因数
for(int i = 2; i <= x / i; i++)
{
while(x % i == 0)
{
x /= i;
primes[i]++;
}
}
if(x > 1) primes[x] ++;
}
LL res = 1; // 存答案
for(auto t : primes) res = res * (t.second + 1) % mod; // 质数加一再连乘 记得模mod
cout << res << endl;
return 0;
}
约数之和
给定 \(n\) 个正整数 \(a_i\),请你输出这些数的乘积的约数之和,答案对 \(10^9+7\) 取模。
公式
\[ (p_1^0 + p_1^1 + \dots + p_1^{c_1}) \times \dots \times (p_k^0 + p_k^1 + \dots + p_k^{c_k})
\]
证明
证明:算术基本定理
一个数\(N\)只能被拆分为唯一的\(p_1^{c_1} \times p_2^{c_2} \times \dots \times p_k^{c_k}\)
所以\(N\)的每个因数就是\(N\)分解质因数后因指数的每一种选法
看公式,在每个括号里面都是\(p_i\)的一种选法,与另一些括号里的\(p_i\)相乘,就是每一个因数,根据乘法分配律,上面的公式就是所有因数之和
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
typedef long long LL; // long long类型
using namespace std;
const int mod = 1e9 + 7; // 模
int main()
{
int n;
scanf("%d", &n);
unordered_map<int, int> primes; // 哈希表
while (n -- )
{
int x;
scanf("%d", &x);
// 分解质因数
for(int i = 2; i <= x / i; i++)
while(x % i == 0)
x /= i, primes[i] ++;
if(x > 1) primes[x] ++;
}
LL res = 1;
for(auto t : primes) // 迭代器简单写法(auto C++ 11标准)
{
LL a = t.first, b = t.second;
LL temp = 1;
// 公式
while(b -- )
temp = (temp * a + 1) % mod;
res = res * temp % mod;
}
cout << res << endl;
return 0;
}
最大公约数
给定 \(n\) 对正整数 \(a_i, b_i\),请你求出每对数的最大公约数。
想法
1.辗转相除
2. algorithm库__gcd()
这是最快的
思路
两个数的gcd等于其中较小的数字和二者之间余数的gcd
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int gcd(int a, int b) // 欧几里得算法
{
return a % b ? gcd(b, a % b) : b;
// return b ? gcd(b, a % b) : a;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- )
{
int a, b;
scanf("%d%d", &a, &b);
cout << gcd(a, b) << endl;
// cout << __gcd(a, b) << endl;
}
return 0;
}