【数论】——约数

【数论】——约数

定义

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

因数分解

试除法

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

一个整数 x 的约数个数最多为:
2 x 2\sqrt x 2x

  • 代码
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] 中每个数的倍数个数表示为:
    n d \frac{n}{d} dn
    则所有数的倍数个数为:
    c n t = N 2 + N 3 + . . . + N N − 1 + N N cnt=\frac{N}{2}+\frac{N}{3}+...+\frac{N}{N-1}+\frac{N}{N} cnt=2N+3N+...+N1N+NN
    c n t = N ( 1 2 + 1 3 + . . . + 1 N − 1 + 1 N ) cnt = N\left(\frac{1}{2}+\frac{1}{3}+...+\frac{1}{N-1}+\frac{1}{N} \right) cnt=N(21+31+...+N11+N1)
    此式为 调和级数:

c n t = ln ⁡ N + C cnt = \ln N+{C} cnt=lnN+C

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

倍数法的推论

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

  • 代码
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 =    p 1 c 1 +    p 2 c 2 + . . . +    p m c m N=\; p_{1}^{c_{1}}+\; p_{2}^{c_{2}}+...+\; p_{m}^{c_{m}} N=p1c1+p2c2+...+pmcm
    其中, c i c_{i} ci 是正整数, p i p_{i} pi 是质数,且随着下标严格增大。
算术基本定理的推论
  1. N 所有因数的个数
    c o u n t O f F a c t e r = ( c 1 + 1 ) ∗ ( c 2 + 2 ) ∗ . . ∗ ( c m + 1 ) countOfFacter = (c_{1}+1)*(c_{2}+2)*..*(c_{m}+1) countOfFacter=(c1+1)(c2+2)..(cm+1)
  • 推导
    每一个    p i c i \; p_{i}^{c_{i}} pici 都可以根据算术基本定理继续分解,因此可以构造出 ( c 1 + 1 ) (c_{1}+1) (c1+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 所有因数的和
    s u m O f F a c t o r = ( 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 ) 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}}) sumOfFactor=(p10+p11+p12+...+p1c1)(p20+p21+p22+...+p2c2)...(pm0+pm1+pm2+...+pmcm)
  • 推导
    1)中说明了每一个    p i c i \; p_{i}^{c_{i}} pici 可以构造出 ( c 1 + 1 ) (c_{1}+1) (c1+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,有:(证明为构造函数法)
    g c d ( a , b ) ∗ l c m ( a , b ) = a ∗ b gcd(a,b) * lcm(a,b) = a*b gcd(a,b)lcm(a,b)=ab

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

  • 对于任意自然数 a 与非零自然数 b:(证明为分 a>b,a≤b 讨论)
    g c d ( a , b ) = g c d ( b , a    m o d    b ) gcd(a,b) = gcd(b,a\;mod\;b) gcd(a,b)=gcd(b,amodb)
  • 代码
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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Siriu_Sky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值