简单数论——约数

Posted apstar

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单数论——约数相关的知识,希望对你有一定的参考价值。

约数

算术基本定理

对于任何一个大于1的自然数N,如果N不为质数,那么都可以唯一分解成有限个质数的乘积N = ({P_1}^{alpha_1} {P_2}^{alpha_2} ... {P_n}^{alpha_n})

这里(P_1<P_2<...<P_n)且均为质数,其诸指数(a_i)是正整数。这样的分解称为N的标准分解式

试除法求一个数所有约数

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;
}

求约数个数

对任意一个数N都可以写成:

N = ({P_1} ^ {alpha_1} {P_2} ^ {alpha_2} ... {P_k} ^ {alpha_k})

对于他的任一约数d = ({P_1} ^ {eta_1} {P_2} ^ {eta_2} ... {P_k} ^ {eta_k})((0 le eta_i le alpha_i))只要指数(eta)不同,d就不同

所以N的约数的个数实际就是(eta_1) ~ (eta_k)的取法个数,其实就是看(eta_k)有多少组合方式,如(eta_k)有(0~(alpha_k))即((alpha_k + 1))种选法

整体N的选法即N的约数个数 = ((alpha_1 + 1) (alpha_2 + 1) ... (alpha_k+1))

代码思路

先求((a_1 a_2 ... a_n))的质因式分解结果

(a_1,a_2,...,a_n)都分别分解,再把每一个数的指数累加到一起就可以了

假设先求出(a_1)的某一个质因子(P_i ^ {alpha_i}),可以用一个Hash表或一个map将他存下来

如map[(P_i)] += (alpha_i)就可以了,最后map就存储了整个乘积的指数和底数

约数个数就是所有指数+1在相乘

/**
 * 给定n个数a_1到a_n,
 * 求出a_1 * a_2 * ... *a_n的约数个数取模1e9+7
 */
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>

using namespace std;

typedef long long LL;

const int N = 110, mod = 1e9 + 7;

int main()
{
    int n;
    cin >> n;

    unordered_map<int, int> primes;

    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] ++ ; // 说明x是一个比较大的质因数,把他记录一下
    }

    LL res = 1;
    for (auto p : primes) res = res * (p.second + 1) % mod;

    cout << res << endl;

    return 0;
}

求约数之和

N = ({P_1} ^ {alpha_1} {P_2} ^ {alpha_2} ... {P_k} ^ {alpha_k})

约数之和 = ((P_1^0 + P_1 ^ 1 + ... + P_1 ^ {alpha_1}) (P_2^0 + P_2 ^ 1 + ... + P_2 ^ {alpha_2}) ... (P_k^0 + P_k ^ 1 + ... + P_k ^ {alpha_k}))

将上述式子用乘法分配律展开共有((alpha_1+1)(alpha_2+1)...(alpha_k+1))种选法

每一个展开的乘积都是((P_1^{eta_1} P_2^{eta_2}...P_k^{eta_k}))的形式,((0 le eta_k le alpha_k))

这里的每一项其实都是一个约数,所以他一共的个数就是约 数的个数,可以发现这个式子展开其实就是把所有约数加到一起了

代码思路

如何求出(P^0 + P^1 + ... P^alpha)呢?

方法有很多,可以初始令t = 1, 每次令t = p*t+1

第一次执行后t=(p^2+p+1)

执行a次后t=(P^0 + P^1 + ... P^alpha)

/**
 * 给定n个数a_1到a_n,
 * 求出a_1 * a_2 * ... *a_n的约数之和取模1e9+7
 */
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>

using namespace std;

typedef long long LL;

const int N = 110, mod = 1e9 + 7;

int main()
{
    int n;
    cin >> n;

    unordered_map<int, int> primes;

    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 (auto p : primes) // 枚举所有的质因数
    {
        LL a = p.first, b = p.second; // p是底数,a是指数
        LL t = 1;
        // 求出P^0+P^1+...+P^n
        while (b -- ) t = (t * a + 1) % mod;
        res = res * t % mod; 
    }

    cout << res << endl;

    return 0;
}

以上是关于简单数论——约数的主要内容,如果未能解决你的问题,请参考以下文章

[数论][筛法求素数]约数

JZYZOJ1379天才的约数和 数论 约数和

B1968 [Ahoi2005]COMMON 约数研究 数论

PASS486|数论|约数|枚举

数论考试题(b) 求约数的约数的最大个数

PAT 数列的片段和简单数论