算法竞赛进阶指南——0x31【质数】


在这里插入图片描述

质数的判定

试除法判断

质数的筛选

暴力筛

ll get_prime(ll n)//暴力筛
{
    ll i, j, cnt = 0, f;
    for (i = 2; i <= n; i++)
    {
        f = 1;//假设i是素数
        for (j = 2; j <= sqrt(i); j++)
        //一个合数n的最小质因子一定小于等于根号n
        {
            if (i % j == 0)
            {
                f = 0;
                break;
            }
        }
        if (f)//若i是素数,放入prime数组
            prime[cnt++] = i;//cnt计数器++
    }
    return cnt;//返回素数的个数
}

埃式筛

using ll = long long;
const ll N = 1e6;
bool isprime[N];   //布尔数组表示是否是素数****
ll prime[N];       //用来存放素数的数组
ll get_prime(ll n) //埃式筛
{
    ll i, f, cnt = 0, j;
    memset(isprime, 1, sizeof(isprime)); //先假设都是素数
    isprime[0] = isprime[1] = 0;   //0,1都不是素数
    for (i = 2; i <= n; i++)   //优化1:i判断到√n即可,此优化有风险,不建议
    {
        if (isprime[i]) //若是素数
        {
            prime[cnt++] = i;//放入素数数组
            for (j = i * i; j <= n; j += i) //优化2:j从i开始更高效,i之前的已经被筛了,防止重复筛
                isprime[j] = 0;//筛掉该素数因子的所有合数
        }
    }
    return cnt; //返回素数的个数
}

欧拉筛

using ll = long long;
const ll N = 1e7;
int prime[N]; //素数数组
bool isprime[N];//表明是否是素数****
ll getprime(ll n)//欧拉筛
{
    ll cnt = 0, i, j;
    memset(isprime, 1, sizeof(isprime));//初始都是素数
    isprime[0] = isprime[1] = 0;//0,1不是素数
    for (i = 2; i < n; i++)
    {//每次循环,倍数i会慢慢变大
        if (isprime[i])//若是素数,则加入素数数组
            prime[cnt++] = i;
        for (j = 0; j < cnt && i * prime[j] < n; j++)//遍历每个素数
        {//i是倍数,prime是素因子
            isprime[i * prime[j]] = 0;//筛去prime的倍数的数
            if (i % prime[j] == 0)//每个数只能让最大倍数*最小素因子数才能保证筛一次
                break;
        }
    }
    return cnt;//返回素数个数
}

牛客一道小栗子Prime Distance

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
const int N = 1e7 + 10;
const int M = 1e7 + 7;
const int inf = 0x3f3f3f3f;
const int eps = 1e-6;
#define IOS                  \
    ios::sync_with_stdio(0); \
    cin.tie(0);              \
    cout.tie(0)
int l, r, tot, cnt;
ll now[N], prime[N];     //注意这里开大啊,N=1e7+10
bool isprime[N], vis[N]; //
inline void get_prime()  //线性筛
{
    for (int i = 2; i < M; i++)
    {
        if (!isprime[i]) //后面没有{},这里0表示是素数,1表示不是素数
            prime[++tot] = i;
        for (int j = 1; j <= tot && i * prime[j] < M; j++) //是&&,不能用,
        {
            isprime[i * prime[j]] = 1;
            if (i % prime[j] == 0)
                break;
        }
    }
}
int main()
{
    // IOS;
    get_prime();
    while (~scanf("%d%d", &l, &r))
    {
        cnt = 0; //
        memset(vis, 0, sizeof(vis));
        for (ll i = 1; i <= tot && prime[i] * prime[i] <= r; i++) //对每个素数而言
        {
            ll x = prime[i];                                               //此处有【向上取整算法:向下取整(l+p-1)/p=向上取整(l/p)】
            for (ll j = max(x * 2ll, (l + x - 1) / x * x); j <= r; j += x) //筛选出l~r之间的素数
                vis[j - l] = 1;                                            //应该从p*2开始,因为p*1的话p是素数,不能标记
        }
        for (ll i = 0; i <= r - l; i++)
        {
            if (l + i <= 1) //特判0,1
                continue;
            if (!vis[i])
                now[++cnt] = i + l; //now是筛选出的素数的集合
        }
        if (cnt < 2)
            printf("There are no adjacent primes.\n");
        else
        {
            pii mi, ma;
            int minn = inf, maxx = -inf;
            for (int i = 1; i < cnt; i++) //注意有i+1,所以i<cnt而不是=
            {
                if (minn > now[i + 1] - now[i])
                {
                    minn = now[i + 1] - now[i];
                    mi.first = now[i], mi.second = now[i + 1];
                }
                if (maxx < now[i + 1] - now[i]) //不能else
                {
                    maxx = now[i + 1] - now[i];
                    ma.first = now[i], ma.second = now[i + 1];
                }
            }
            printf("%d,%d are closest, %d,%d are most distant.\n", mi.first, mi.second, ma.first, ma.second);
        }
    }
    return 0;
}

质因数分解

算术基本定理:任何一个大于1的正整数都能唯一分解为有限个质数的乘积即N=p1^c1* p2^c2 * … * pm^cm
推论1:N的正约数个数为(c1+1)* (c2+1) * … * (cm+1)
推论2:N的正约数的和为(1+p1+p12+…+p1c1)*…(1+pm+pm2+…+pmcm)
分解单个数字:

get_prime(n);//欧拉筛
for (int i = 1; i <= tot; i++)//枚举每个数组
{
    if (n % prime[i] == 0)
    {//first代表质因子,second代表质因子的次数
        ans[++cnt].first = prime[i], ans[cnt].second = 0;
        while (n % prime[i] == 0)
        {
            n /= prime[i];
            ans[cnt].second++;
        }
    }
}
if (n > 1)//n是质数
    ans[++cnt].first = n, ans[cnt].second = 1;
for (int i = 1; i <= cnt; i++)
cout << ans[i].first << ' ' << ans[i].second << endl

牛客一个小栗子阶乘分解

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
const int N = 1e6 + 10;
const int M = 1e7 + 7;
const int inf = 0x3f3f3f3f;
const int eps = 1e-6;
#define IOS                  \
    ios::sync_with_stdio(0); \
    cin.tie(0);              \
    cout.tie(0)
int n, tot, cnt;
bool noprime[N];
int prime[N];
pii ans[N];
inline void get_prime(int n) //欧拉筛
{
    noprime[0] = noprime[1] = 1;
    for (int i = 2; i <= n; i++) //
    {
        if (!noprime[i])
            prime[++tot] = i;
        for (int j = 1; j <= tot && i * prime[j] <= n; j++)
        {
            noprime[i * prime[j]] = 1;
            if (!i % prime[j])
                break;
        }
    }
}
inline ll fast_pow(int a, int b) //快速幂
{
    ll res = 1;
    for (; b; b >>= 1)
    {
        if (b & 1)
            res *= a;
        a *= a;
    }
    return res;
}
int main()
{
    IOS;
    cin >> n;
    get_prime(n);                  //欧拉筛
    for (int i = 1; i <= tot; i++) //枚举每个数组
    {
        ans[++cnt].first = prime[i]; //用到了log函数【log(n)/log(m)表示以m为底,n的对数】
        for (int k = 1; k <= int(log(n) / log(prime[i])); k++)
            ans[cnt].second += int(n / fast_pow(prime[i], k));
    } //推导的结论【N!中质因子p的个数为Σ向下取整[N/(p^k)],其中1≤k≤log(N)/log(p)】
    for (int i = 1; i <= cnt; i++)
        cout << ans[i].first << ' ' << ans[i].second << endl;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WTcrazy _

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

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

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

打赏作者

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

抵扣说明:

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

余额充值