数论——约数问题

一、一个数的约数个数有多少

\quad 取个例子,对于12而言,其约数有1,2,3,4,6,126个不同的约数。怎么快速的求出一个数的约数数目有多少个呢?可以对这个数进行质因子分解,分解成 N = p 1 α 1 p 2 α 2 ⋯ p n α n N=p_1^{\alpha_1}p_2^{\alpha_2}\cdots p_n^{\alpha_n} N=p1α1p2α2pnαn,这样其约数个数为 ( α 1 + 1 ) ∗ ( α 2 + 1 ) ∗ ( α n + 1 ) (\alpha_1+1)*(\alpha_2+1)*(\alpha_n+1) (α1+1)(α2+1)(αn+1)。理解起来也很简单,任意取一个数的质因子 p i p_i pi 0   α i 0~\alpha_i 0 αi次,将这些质因子相乘得到的结果即为这个数的一个约数。比如 12 = 2 2 ∗ 3 1 12=2^2*3^1 12=2231

  • 0 个 2 和 0 个 3 , 得 约 数 1 0个2和0个3,得约数1 02031
  • 1 个 2 和 0 个 3 , 得 约 数 2 1个2和0个3,得约数2 12032
  • 2 个 2 和 0 个 3 , 得 约 数 4 2个2和0个3,得约数4 22034
  • 0 个 2 和 1 个 3 , 得 约 数 3 0个2和1个3,得约数3 02133
  • 1 个 2 和 1 个 3 , 得 约 数 6 1个2和1个3,得约数6 12136
  • 2 个 2 和 1 个 3 , 得 约 数 12 2个2和1个3,得约数12 221312

这样就可以轻松求出这个数所有的约数。
在这里插入图片描述

#include <iostream>
#include <map>
using namespace std;

const int MOD = 1e9 + 7;
int main()
{
    map<int, int> cnt; // 存放每个质因子出现次数
    int num; cin >> num;
    while(num -- )
    {
        int n; cin >> n;
        for(int i = 2; i * i <= n; i ++ )
            while(n % i == 0) n /= i, cnt[i] ++ ;
        if(n > 1) cnt[n] ++ ;
    }
    long long res = 1;
    for(auto &[p, a] : cnt) res = res * (a + 1) % MOD;
    cout << res << endl;
    return 0;
}

二、约数之和

\quad 继上面这个问题,再求解约数的和。对于 12 = 2 2 ∗ 3 1 12=2^2*3^1 12=2231来说,其约数之和为 2 0 ∗ 3 0 + 2 1 ∗ 3 0 + 2 2 ∗ 3 0 + 2 0 ∗ 3 1 + 2 1 ∗ 3 1 + 2 2 ∗ 3 1 = ( 2 0 + 2 1 + 2 2 ) ∗ ( 3 0 + 3 1 ) 2^0*3^0+2^1*3^0+2^2*3^0+2^0*3^1+2^1*3^1+2^2*3^1=(2^0+2^1+2^2)*(3^0+3^1) 2030+2130+2230+2031+2131+2231=(20+21+22)(30+31)。借助这个例子,我们轻易可得:
对于任意正整数 N = p 1 α 1 p 2 α 2 ⋯ p n α n N=p_1^{\alpha_1}p_2^{\alpha_2}\cdots p_n^{\alpha_n} N=p1α1p2α2pnαn,其约数之和为 ( p 1 0 + ⋯ + p 1 α 1 − 1 ) ∗ ⋯ ∗ ( p n 0 + ⋯ + p n α n − 1 ) (p_1^{0}+\cdots+p_1^{\alpha_1-1})*\cdots*(p_n^{0}+\cdots+p_n^{\alpha_n-1}) (p10++p1α11)(pn0++pnαn1)。给个例题:
在这里插入图片描述

#include <iostream>
#include <map>
using namespace std;

const int MOD = 1e9 + 7;
int main()
{
    int n; cin >> n;
    map<int, int> cnt;
    while(n -- )
    {
        int t; cin >> t;
        for(int i = 2; i * i <= t; i ++ )
            while(t % i == 0) t /= i, cnt[i] ++ ;
        if(t > 1) cnt[t] ++ ;
    }
    long long res = 1;
    for(auto &[base, a] : cnt)
    {
        long long temp = 1, p = base;
        for(int i = 0; i < a; i ++ ) temp = (temp + p) % MOD, p = base * p % MOD;
        res = res * temp % MOD;
    }
    cout << res << endl;
    return 0;
}

三、求1~n中每个数的约数的和 O ( n ) O(\sqrt{n}) O(n )

\quad 输入一个整数n,输出 1 , 2 , . . . , n 1,2,...,n 1,2,...,n中每个数的约数的和。例如当n=3时,1有1个约数1;2有2个约数1,2;3有2个约数1,3。故而输出为5。下面给出时间复杂度为 O ( n ) O(\sqrt{n}) O(n )的做法。
\quad 首先我们可以得到约数和公式 y = n 1 + n 2 + ⋯ + n n y=\frac{n}{1} + \frac{n}{2} + \cdots + \frac{n}{n} y=1n+2n++nn,直接这样做时间复杂度为 O ( n ) O(n) O(n)。我们将公式看做一个函数: y = ∑ x = 1 x = n n x , x 为 正 整 数 y=\sum_{x=1}^{x=n}{\frac{n}{x}},x为正整数 y=x=1x=nxn,x。这样相当于求 y = n x y=\frac{n}{x} y=xn这个曲线与x轴围成的面积(只是相当于,毕竟这是离散点)。 y = n x y=\frac{n}{x} y=xn这个曲线关于 y = x y=x y=x对称,交点在 x \sqrt{x} x 处。故而其面积可以分为三部分计算:

  • y = x , y 轴 和 y = n x y=\sqrt{x},y轴和y=\frac{n}{x} y=x yy=xn围成的部分 s 1 s1 s1
  • y = x , x = x , x 轴 和 y 轴 y=\sqrt{x},x=\sqrt{x},x轴和y轴 y=x ,x=x ,xy围成的部分 s 2 s2 s2
  • x = x , x 轴 和 y = n x x = \sqrt{x},x轴和y=\frac{n}{x} x=x xy=xn围成的部分 s 3 s3 s3

\quad 由于 y = n x y=\frac{n}{x} y=xn关于 y = x y=x y=x对称,故而 s 1 = s 3 s1=s3 s1=s3 s 1 + s 2 s1+s2 s1+s2的面积即为 ∑ x = 1 x = n n x , x 为 正 整 数 \sum_{x=1}^{x=\sqrt{n}}\frac{n}{x},x为正整数 x=1x=n xnx s 2 s2 s2面积为 i n t ( x ) ∗ i n t ( x ) int(\sqrt{x})*int(\sqrt{x}) int(x )int(x )。令 t = i n t ( x ) t=int(\sqrt{x}) t=int(x )最终可得 y = s 1 + s 2 + s 3 = 2 ∗ ∑ x = 1 x = t n x − t ∗ t y=s1+s2+s3=2*\sum_{x=1}^{x=t}\frac{n}{x}-t*t y=s1+s2+s3=2x=1x=txntt。时间复杂度为 O ( n ) O(\sqrt{n}) O(n )

long long solve(long long n) {
    long long sum = 0;
    int t = sqrt(n);
    for (int i = 1; i <= t; i++) sum += n / i;  // s1 + s2 面积
    sum *= 2;  // 2 * s1 + 2 * s2 面积
    sum -= t * t;  // 2 * s1 + s2 = s1 + s2 + s3
    return sum;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值