【数论】——约数「与约数相关的内容都在这里了,快收藏吧!」

145 阅读2分钟

【数论】——约数

定义

对一个整数 n 除以整数 d,余数 r 为 0,则称 dn 的一个约数,nd 的一个倍数,记为:

dnd|n

因数分解

试除法

  • x 的因数,即从 1 开始向后扫描,如果 x%i == 0,则将 ix/i 记为 x 的一对因数。
  • 优化: 由于合数的因数对关于 x\sqrt x 对称,扫描到 x 会把每一对因数算 2 次,因此只需要扫描 [1,x\sqrt x] 即可得到 x 的所有因数。

试除法的推论

一个整数 x 的约数个数最多为:

2x2\sqrt x
  • 代码
int factor[N], cnt = 0;//factor 存所有因数,cnt 表示个数
void get_factor(int x)
{
    /*
         这里建议写 i<=x/i
            原因:
            1)写 sqrt(x),要调用 <cmath> 的函数,太慢、
            2)写 i*i<=x, 如果 i 过大会溢出。
    */

    for (int i = 1; i <= i / n; i++) {if (x % i == 0) {factor[++cnt] = i;// 加入 i
            if (i != x / i)// 如果 i 不是 √x,加入 x/i
                factor[++cnt] = x / i;
        }
    }
}

倍数法求区间内数的所有因数

  • [1,n] 中所有数的因素,等价于对于 [1,n] 中每个数 d,求在 [1,n] 中所有以 d 为约数的数
  • 复杂度分析 将 [1,n] 中每个数的倍数个数表示为:
nd\frac{n}{d}

则所有数的倍数个数为:

cnt=N2+N3+...+NN1+NNcnt=\frac{N}{2}+\frac{N}{3}+...+\frac{N}{N-1}+\frac{N}{N}
cnt=N(12+13+...+1N1+1N)cnt = N\left(\frac{1}{2}+\frac{1}{3}+...+\frac{1}{N-1}+\frac{1}{N} \right)

此式为 调和级数:

cnt=lnN+Ccnt = \ln N+{C}

因此复杂度为:O(n(lnn)n\cdot \left( \ln n \right))

倍数法的推论

对于区间 [1,n] 中所有数的约数个数总和为:

NlogNN\log N
  • 代码
vector<int> factors[N];//factor[i] 存储数 i 的所有因数
void get_factors(int n)
{
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n / i; j++)// 防止重复,j 扫描到 n/i
            factor[i * j].push_back(i);
    // 打印:
    for (int i = 1; i <= n; i++) {for (auto i : factor[i])
            printf("%d", i);
        puts("");
    }
}

质因数分解

算术基本定理

  • 任意正整数 x 大于 1,能被分解为有限个质数的乘积:
N=  p1c1+  p2c2+...+  pmcmN=\; p_{1}^{c_{1}}+\; p_{2}^{c_{2}}+...+\; p_{m}^{c_{m}}

其中,cic_{i} 是正整数,pip_{i} 是质数,且随着下标严格增大。

算术基本定理的推论

  1. N 所有因数的个数
countOfFactor=(c1+1)(c2+2)..(cm+1)countOfFactor = (c_{1}+1)*(c_{2}+2)*..*(c_{m}+1)
  • 推导 每一个   pici\; p_{i}^{c_{i}} 都可以根据算术基本定理继续分解,因此可以构造出 (c1+1)(c_{1}+1) 个因数,再由乘法原理,最终可以得到总共的因数个数为上式。
  • 代码
const int mod = 1e9 + 7;
unordered_map<int, int> primes; // 用来存(pi,ci)
int countOfFactor(int x)
{
    for (int i = 2; i <= x / i; i++) {if (x % i == 0) {
            int c = 0;
            while (x % i == 0) {
                c++;
                x /= i;
            }
            primes[i] = c; // 记录(pi,ci)
        }
    }
    // 如果 x 不为 1,则 x 为大于√x 的一个质因数
    if (x > 1)
        primes[x]++;
    int res = 1;
    for (auto i : primes)
        res = (long long)res * (i.second + 1) % mod;

    return res;
}
  1. N 所有因数的和
sumOfFactor=(p10+p11+  p12+...+  p1c1)(p20+p21+  p22+...+  p2c2)...(pm0+pm1+  pm2+...+  pmcm)sumOfFactor = (p_{1}^0+p_{1}^1+\;p_{1}^2+...+\;p_{1}^{c_{1}})*(p_{2}^0+p_{2}^1+\;p_{2}^2+...+\;p_{2}^{c_{2}})*...*(p_{m}^0+p_{m}^1+\;p_{m}^2+...+\;p_{m}^{c_{m}})
  • 推导 1)中说明了每一个   pici\; p_{i}^{c_{i}} 可以构造出 (c1+1)(c_{1}+1) 个因数,构造每一个因数(括号内的部分),再由乘法原理即可求出所有因数的和。
  • 代码
const int N = 1e9 + 7;
unordered_map<int, int> primes; // 用来存(pi,ci)
int countOfFactor(int x)
{
    for (int i = 2; i <= x / i; i++) {if (x % i == 0) {
            int c = 0;
            while (x % i == 0) {
                c++;
                x /= i;
            }
            primes[i] = c;// 记录(pi,ci)
        }
    }
    // 如果 x 不为 1,则 x 为大于√x 的一个质因数
    if (x > 1)
        primes[x]++;
    int res = 1;
    for (auto i : primes){
        int p = i.first, c = i.second;
        int tmp = 1;
        // 计算每个括号
        while (c--) {tmp = (long long)(tmp * p + 1) % N;
        }
        res = (long long)res * tmp % N;
    }
    return res;
}

最大公约数 / 最小公倍数(gcd & lcm)

定义

  • 若存在自然数 d 同时是自然数 a,b 的约数,则称 da,b 的公约数,其中最大的满足以上条件的 d,称为最大公约数,记为 gcd(a,b)
  • 若存在自然数 m 同时是自然数 a,b 的倍数,则称 ma,b 的公倍数,其中最小的满足以上条件的 d,称为最小公倍数,记为 lcm(a,b)

乘积定理

  • 对于任意的自然数 a,b,有:(证明为构造函数法)
gcd(a,b)lcm(a,b)=abgcd(a,b) * lcm(a,b) = a*b

欧几里得算法(辗转相除法)

  • 对于任意自然数 a 与非零自然数 b:(证明为分 a>b,a≤b 讨论)
gcd(a,b)=gcd(b,a  mod  b)gcd(a,b) = gcd(b,a\;mod\;b)
  • 代码
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}
  • 顺便贴上扩展欧几里得(用于求解线性同余方程)
int gcd(int a, int b, int& x, int& y)
{
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    int d = gcd(b, a % b, x, y);
    int t = x;
    x = y;
    y = t - y * (a / b);
    return d;
}