约数个数与约数和专题
一、约数个数公式
如果\(n\)的唯一分解式: \(n={p_1}^{r_1} {p_2}^{r_2} ... {p_k}^{r_k}\)
一、\(n\)的约数个数公式:
证明:以\(p_1\)为例,这个质数因子,可以选择\(0\)个,可以选择\(1\)个,...,最多可以选择\(r_1\)个,就是有\(r_1+1\)种选择的可能性,其它\(p_2,p_3,...,p_k\)都是如此,根据乘法原理,所有的可能性就是\((r_1+1) * (r_2+1) * ... * (r_k+1)\)。
举个栗子:
\(180= 2^2 * 3^2 * 5\)
约数个数\(=(1+2) * (1+2) * (1+1) =18\)
二、求单个数字的约数个数
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
/**
* 功能:计算约数个数
* @param n
* @return
*/
LL getDivisorCount(LL x) {
unordered_map<int, int> primes; //key:质数 value:个数
//求质数因子
for (int i = 2; i <= x / i; i++)
while (x % i == 0) x /= i, primes[i]++; //primes[i]表示质数i因子的个数+1
//如果还有质数,那就加上
if (x > 1) primes[x]++;
//公式大法
LL res = 1;
for (auto p: primes) res = res * (p.second + 1);
return res;
}
LL res;
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) cout << i << " " << getDivisorCount(i) << endl;
return 0;
}
三、求数字连乘积的约数个数
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 110;
const int mod = 1e9 + 7;
//记录质数因子及个数
unordered_map<int, int> primes;
int n;
int main() {
cin >> n;
while (n--) {
int x;
cin >> 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 (PII p: primes) res = res * (p.second + 1) % mod;
//输出结果
cout << res << endl;
return 0;
}
四、约数和公式
- 约数和公式
\(\sigma(n)=({p_1}^{0}+{p_1}^{1}+{p_1}^{2}+...+{p_1}^{r_1})({p_2}^{0}+{p_2}^{1}+{p_2}^{2}+...+{p_2}^{r_2})...({p_k}^{0}+{p_k}^{1}+{p_k}^{2}+...+{p_k}^{r_k})\)
Sigma(大写Σ,小写σ),是第十八个希腊字母。
举个栗子:
\(180= 2^2 * 3^2 * 5\)
约数和\(=(1+2+4) * (1+3+9 ) * (1+5)=546\)
证明:
和上面证明个数一样的思路,每个质数因子可以选择\(0\sim r_k\)个,以上面\(180\)为例,第一项质数因子\(2\),可以不要,也可以要\(1\)或\(2\)个,每种选择情况,因为还要和后面的式子进行乘积,不要的话,理解为\(2^0\);\(1\)个的话理解为\(2^1\);\(2\)个的话理解为\(2^2\),每项都按这个规则拆开,就是上面的公式了,证毕。
五、求单个数字的约数和
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
/**
* 功能:计算约数之和
* @param n
* @return
*/
LL getSumOfDivisors(LL x) {
//拆出所有质数因子及质数因子个数
unordered_map<int, int> primes;
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 p : primes) {
LL a = p.first, b = p.second;
LL t = 1;
while (b--) t = (t * a + 1);
res = res * t;
}
return res;
}
LL res;
int main() {
//测试用例:180
//参考答案:546
int n;
cin >> n;
cout<<getSumOfDivisors(n) << endl;
return 0;
}
六、筛法求区间约数个数和约数和
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
/**
* 功能:线性筛出约数个数与约数和
* Tag:模板,约数个数,约数和
*/
const int N = 1e6 + 10;
int n;
int primes[N]; //质数数组
int idx; //质数数组下标游标
bool st[N]; //是否已被筛出
int d[N]; //约数个数数组
int sigma[N]; //约数和数组
void get_divisor(int n) {
//积性函数的出发值
d[1] = sigma[1] = 1;
for (int i = 2; i <= n; i++) { //倍数
if (!st[i])primes[++idx] = i, d[i] = 2, sigma[i] = i + 1;
for (int j = 1; i * primes[j] <= n & j <= idx; j++) {
st[i * primes[j]] = true;
d[i * primes[j]] = d[i] << 1;
sigma[i * primes[j]] = sigma[i] * (primes[j] + 1);
if (i % primes[j] == 0) {
d[i * primes[j]] -= d[i / primes[j]];
sigma[i * primes[j]] -= primes[j] * sigma[i / primes[j]];
break;
}
}
}
}
LL res;
int main() {
cin >> n;
//开始筛约数个数,约数和
get_divisor(n);
//输出约数个数和
for (int i = 1; i <= n; i++) res += d[i];
cout << res << endl;
return 0;
}