约数(试除法求约数,约数个数,约数之和,最大公约数)

本文探讨了如何使用试除法高效地计算一个数的约数,包括约数个数和约数之和的计算方法,特别强调了处理平方数的特判。通过核心代码实例和模板展示,讲解了如何利用质因数分解来简化计数过程。同时介绍了欧几里得算法在最大公约数求解中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

试除法求约数 O ( n ) O(\sqrt{n}) O(n )

需要注意的是如果一个数是平方数的话需要特判一下只加一个进去
模板题:AcWing 869. 试除法求约数

核心代码如下:

for(int i = 1; i <= a / i; ++i)
{
    if(a % i == 0)
    {
        ans.push_back(i);
        if(i != a / i)
            ans.push_back(a / i);
    }
}

约数个数

N = p 1 a 1 ∗ p 2 a 2 ∗ p 3 a 3 ⋅ ⋅ ⋅ p n a n , 其 中 p 是 质 因 子 N=p_1^{a_1}*p_2^{a_2}*p_3^{a_3}···p_n^{a_n},其中p是质因子 N=p1a1p2a2p3a3pnanp
那么约数个数 c n t = ( a 1 + 1 ) ( a 2 + 1 ) ( a 3 + 1 ) ⋅ ⋅ ⋅ ( a n + 1 ) cnt=(a_1+1)(a_2+1)(a_3+1)···(a_n+1) cnt=(a1+1)(a2+1)(a3+1)(an+1)

模板题:AcWing 870. 约数个数

模板代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 1e9 + 7;
int n, a;

int main()
{
    map<int, int> m;
    scanf("%d", &n);
    while(n--)
    {
        scanf("%d", &a);
        for(int i = 2; i <= a / i; ++i)
        {
            if(a % i == 0)
            {
                while(a % i == 0)
                {
                    ++m[i];
                    a /= i;
                }
            }
        }
        if(a>1)//记得这里还要考虑如果还有一个比较大的质因数
            ++m[a];
    }

    ll res = 1;
    for(auto it: m)
        res = res * (1 + it.second) % mod;

    printf("%lld", res);
    return 0;
}

约数之和

N = p 1 a 1 ∗ p 2 a 2 ∗ p 3 a 3 ⋅ ⋅ ⋅ p n a n , 其 中 p 是 质 因 子 N=p_1^{a_1}*p_2^{a_2}*p_3^{a_3}···p_n^{a_n},其中p是质因子 N=p1a1p2a2p3a3pnanp
N N N 的所有约数之和
s u m = ( p 1 0 + p 1 1 + p 1 2 + ⋅ ⋅ ⋅ + p 1 a 1 ) ( p 2 0 + p 2 1 + p 2 2 + ⋅ ⋅ ⋅ + p 2 a 2 ) ⋅ ⋅ ⋅ ( p n 0 + p n 1 + p n 2 + ⋅ ⋅ ⋅ + p n a n ) sum=(p_1^0+p_1^1+p_1^2+···+p_1^{a_1})(p_2^0+p_2^1+p_2^2+···+p_2^{a_2})···(p_n^0+p_n^1+p_n^2+···+p_n^{a_n}) sum=(p10+p11+p12++p1a1)(p20+p21+p22++p2a2)(pn0+pn1+pn2++pnan)

模板题:AcWing 871. 约数之和

下面这是个运算技巧
w h i l e ( a − − ) while(a--) while(a)
t = ( t ∗ b + 1 ) % m o d ; t=(t*b+1)\%mod; t=(tb+1)%mod;

AC代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const int mod=1e9+7;

int n,a;

int main()
{
    scanf("%d",&n);
    map<int,int>m;
    while(n--)
    {
        scanf("%d",&a);
        for(int i=2;i<=a/i;++i)
            if(a%i==0)
            {
                while(a%i==0)
                {
                    ++m[i];
                    a/=i;
                }
            }
            
        if(a>1)
            ++m[a];
    }
    
    ll res=1;
    for(auto x:m)
    {
        int b=x.first,a=x.second;
        ll t=1;
        while(a--)
            t=(t*b+1)%mod;
        res=res*t%mod;
    }
    printf("%lld",res);
    return 0;
}

用递归来求如下:

#include <bits/stdc++.h>

using namespace std;
#define ll long long
const int mod = 1e9 + 7;
int n, a;

ll qmi(ll p, int k)
{
    p %= mod;
    ll ans = 1;
    while(k)
    {
        if(k & 1)
            ans = ans * p % mod;
        p = p * p % mod;
        k >>= 1;
    }
    return ans;
}

ll sum(int p, int k)
{

    if(k == 0)
        return 1;
    if(k & 1)//如果是奇数次幂
        return ((1 + qmi(p, k / 2 + 1)) * sum(p, k / 2)) % mod;
    else//如果是偶数次幂
        return (p % mod * sum(p, k - 1) + 1) % mod;
}

int main()
{
    map<int, int> m;
    scanf("%d", &n);
    while(n--)
    {
        scanf("%d", &a);
        for(int i = 2; i <= a / i; ++i)
        {
            if(a % i == 0)
            {
                while(a % i == 0)
                {
                    ++m[i];
                    a /= i;
                }
            }
        }
        if(a > 1)
            ++m[a];
    }

    ll res = 1;
    for(auto it: m)
        res = sum(it.first, it.second) * res % mod;

    printf("%lld", res);
    return 0;
}

最大公约数

欧几里得算法==辗转相除法

如果 d   ∣   a d \,|\, a da,并且 d   ∣   b d\,|\, b db,那么有 d   ∣   a x   +   b y   d\,|\,ax\,+\,by\, dax+by
所以 ( a , b ) = ( b , a   m o d   b ) (a,b)=(b,a \,mod \,b) (a,b)=(b,amodb)
理由如下:
( b , a   m o d   b ) (b,a \,mod \,b) (b,amodb)等价于 ( b , a − c ∗ b ) (b,a-c*b) (b,acb) c c c 是一个整数
( a , b ) (a,b) (a,b) ( b , a − c ∗ b ) (b,a-c*b) (b,acb)可以由倍数互相转换
模板题:AcWing 872. 最大公约数
核心代码如下:

int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;//gcd()经过变换之后里面一定是左大右小
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值