算法--质数和约数

算法基础系列


算术基本定理

公理:任何一个大于1的自然数 N,如果 N 不为质数,那么 N 可以唯一分解成有限个质数的乘积
公式: N = P 1 d 1 + P 2 d 2 + ⋯ + P n d n , d > 0 N=P_1^{d_1}+P_2^{d_2}+\cdots+P_n^{d_n},d>0 N=P1d1+P2d2++Pndnd>0

	int n;
    cin >> n;

    unordered_map<int, int> primes;
	//用map存,用auto来写
	//另一个方法见下文线性筛
    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] ++ ;
    }

多重集的排列数问题

质数问题

判断质数

方法:试除法
时间复杂度: O ( n ) O(\sqrt n) O(n )(一定是)

bool is_prime(int x)
{
    if (x < 2)
        return false;
    for (int i = 2; i <= x / i; i++)//最优写法 防止溢出
        if (x % i == 0)
            return false;
    return true;
}

分解质因数

方法:试除法
时间复杂度:最坏是 O ( log ⁡ n ) O(\log_{}{n}) O(logn),最好是 O ( n ) O(\sqrt n) O(n )

优化:从 O ( n ) O(n) O(n)降低到 O ( n ) O(\sqrt n) O(n )
原理: n n n 当中最多只包含一个大于 n \sqrt n n 的质因子

模板

void divide(int x)
{
    for (int i = 2; i <= x / i; i++)
    {
        if (x % i == 0)
        {
            int s = 0;
            while (x % i == 0)
                x /= i, s++;
            cout << i << ' ' << s << endl;
        }
    }
    if (x > 1)
        cout << x << ' ' << 1 << endl;
    puts("");
}

筛法

原理:在 2 ∽ n 2\backsim n 2n中,从 2 开始,删掉每一个质数的倍数,最后剩下的即使质数

最常用:线性筛

朴素筛——枚举每一个数

int prime[N],cnt;
bool st[N];

void get_prime(int x)
{
    for (int i = 2; i <= x; i++)
    {
        if(!st[i])
            prime[cnt++] = x;
        for (int j = i + i; j <= x; j += i)
            st[j] = true;
    }
}

埃氏筛法
时间复杂度: O ( n log ⁡ log ⁡ n ) O(n\log_{}{\log_{}{n}}) O(nloglogn)

int prime[N], cnt;
bool st[N];

void get_prime(int x)
{
    for (int i = 2; i <= x; i++)
    {
        if (!st[i])
        {
            prime[cnt++] = x;
            for (int j = i + i; j <= x; j += i)
                st[j] = true;
        }
    }
}

线性筛法
时间复杂度: O ( n ) O(n) O(n)
该方法能在在 O ( n ) O(n) O(n) 内,求出 1 ∽ n 1\backsim n 1n 之间有所有质数以及每个数的最小质因子

int prime[N], cnt;
bool st[N];

void get_prime(int x)
{
    for (int i = 2; i <= x; i++)
    {
        if (!st[i])
            prime[cnt++] = i;
        for (int j = 0; prime[j] <= x / i; j ++)
        {
            st[prime[j] * i] = true;
            if(i % prime[j] == 0 )
                break;//prime[j] 一定是i的最小质因子
        }
    }
}

注意:当数据量是106 时,埃氏筛法和线性筛法时间差不多
当数据量为107 时,线性筛法速度是埃氏筛法的两倍

约数问题

求所有约数

方法:试除法
模板代码

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 = P 1 d 1 + P 2 d 2 + ⋯ + P n d n , d > 0 N=P_1^{d_1}+P_2^{d_2}+\cdots+P_n^{d_n},d>0 N=P1d1+P2d2++Pndnd>0

源自算术基本定理
约数个数公式: S = ( d 1 + 1 ) + ( d 2 + 1 ) + ⋯ + ( d n + 1 ) S=(d_1+1)+(d_2+1)+\cdots+(d_n+1) S=(d1+1)+(d2+1)++(dn+1)

约数之和

算术基本定理公式: N = P 1 d 1 + P 2 d 2 + ⋯ + P n d n , d > 0 N=P_1^{d_1}+P_2^{d_2}+\cdots+P_n^{d_n},d>0 N=P1d1+P2d2++Pndnd>0

约数之和公式: S = ( 1 + P 1 0 + P 1 1 + ⋯ + P 1 d 1 ) ( 1 + P 2 0 + ⋯ + P 2 d 2 ) ⋯ ( 1 + P k 0 + ⋯ + P k d k ) S=(1+P_1^0+P_1^1+\cdots+P_1^{d_1})(1+P_2^0+\cdots+P_2^{d_2})\cdots(1+P_k^0+\cdots+P_k^{d_k}) S=(1+P10+P11++P1d1)(1+P20++P2d2)(1+Pk0++Pkdk)

约数个数和约数之后的共同模板

#include <bits/stdc++.h>

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 prime : primes)
    {
        LL p = prime.first, d = prime.second;
        LL t = 1;
        while(d--)
            t = (t * p + 1) % mod;
        res = res * t % mod;
    }
	//约数个数
	LL res = 1;
    for (auto prime : primes)
        res = res * (prime.second + 1) % mod;
    cout << res << endl;
    return 0;
}

最大公约数gcd

时间复杂度: O ( log ⁡ n ) O(\log_{n}) O(logn)
欧几里得算法 – 辗转相除法 gcd(a,b)
(a,b) 的最大公约数 = = = (b,a mod b) 理论上最多转换

模板

int gcd(int a,int b)
{
	return b ? gcd(b,a % b) : a;
}

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Tancy.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值