约数个数与约数和专题

一、约数个数公式

如果\(n\)的唯一分解式: \(n={p_1}^{r_1} {p_2}^{r_2} ... {p_k}^{r_k}\)

一、\(n\)的约数个数公式:

$d(n) = (r_1+1) * (r_2+1) * ... * (r_k+1) $

证明:以\(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;
}
posted @ 2021-09-01 14:57  糖豆爸爸  阅读(337)  评论(0编辑  收藏  举报
Live2D