数论 _ 质数和约数

质数

判定质数

试除法判断素数
复杂度: \(O ( \sqrt n )\) (每次一定是\(\sqrt 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;
}

注意点:
for循环的条件建议是: i<= x / i;
因为

  1. i*i <= x 可能i*i会爆int(long long)
  2. i<=sqrt(x) 每次调用sqrt函数可能会超时。

分解质因数 | 求解最大质因数

暴力写法:
就是枚举从2到x的每一个数,如果是x的因子,那么就除去他。
需要注意的是:
因为一个合数的质因子一定在扫描到这个合数之前就从x中剔除掉了,所以无需验证 i 是否为质数

代码:

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

然后有条性质:
n的因子中,最多只包含一个大于sqrt (n) 的质因子。
证明: 如果有两个,那么这两个质数相乘就大于n了。

所以我们可以将代码改为:
暴力2到\(\sqrt n\) ,然后单独处理大于\(\sqrt n\)的那一个。

模板代码

上限复杂度: \(O ( \sqrt n )\)
下限复杂度: \(O ( \log 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;
    cout << endl;
}

筛质数

欧拉线性质数筛

原理

基本思想 :
在埃氏筛法的基础上,让每个合数只被它的 最小质因子×另一个数 筛选一次,以达到不重复的目的。

关键:
if(i%prime[j]==0)break;
理解:

  • 当 i是prime[j]的倍数时,$I_1 =k × prime[j] $
  • 如果继续运算 j+1,就会筛掉

\[I_1 × prime[j + 1] = (k × prime[j] ) × prime[j+1] \]

  • \(I_2 = k × prime[j+1]\)时,还会运行到

\[I_2 × prime[j ] = (k × prime[j+1]) × prime[j] \]

  • 就重复筛了,所以跳出循环,保证每个数只筛选一次。

比如说:
对于筛选90=5×3×3×2=45×2。我们目的是保证90只被他的最小质因子2筛选一次。

当i=30=5×3×2时,因为30的最小质因数是2,
所以当质数为3时、筛选的数是30×3=90,
而对于30×3:因为 30的最小质因数是2 所以一定可以找到30×3=另一个数×2
所以当i%prime[j]==0时,需要结束掉。

代码

int primes[N], top=0;//存储素数
bool st[N];//记录是否为素数
void get_prime(int n)
{
    st[1] = true;
    for (int i = 2; i <= n; i++)
    {
        if (!st[i])   primes[top++] = i;
        for (int j = 0; j *i <=n && j < top; j++)
        {
            st[i * primes[j]] = true;
            if (i % primes[j]==0)//关键
                break;
        }
    }
}

代码可以再简单一下把j < top这个条件去掉。

int primes[N], top=0;//存储素数
bool st[N];//记录是否为素数
void get_prime(int n)
{
    st[1] = true;
    for (int i = 2; i <= n; i++)
    {
        if (!st[i])   primes[top++] = i;
        for (int j = 0; j *i <=n ; j++)
        {
            st[i * primes[j]] = true;
            if (i % primes[j]==0)//关键
                break;
        }
    }
}

素数定理

[1,N]中素数的个数约为NlgN。
则从[1,N]中人选一个数,其为质数的概率为1/lgN。

算数基本定理

任意一个整数都能被分解为如下形式:
\(n=p^{k_{1}}_{1}×p^{k_{2}}_{2}×....×p^{k_{t}}_{t}\)其中p为质数。

约数

小知识:
int 范围内约数最多的数的个数为 1536,那个数为 1745944200

求所有约数

试除法求约数
和求质因子差不多,直接看代码
复杂度:\(O(\sqrt n )\)
代码:

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^{k_{1}}_{1}×p^{k_{2}}_{2}×....×p^{k_{t}}_{t}\)其中p为质数。

如果n被分解成上面的形式,由乘法原理,得约数个数就是

\[num=(k_{1}+1)(k_{2}+1)(k_{3}+1)...(k_{t}+1) \]

应用:求n个数乘积的约数个数

给定 n 个正整数 ai,请你输出这些数的乘积的约数个数
思路:
分别求出每一个的,然后把每一个数的指数累加即可。
代码:

#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>

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 p : primes) res = res * (p.second + 1) % mod;

    cout << res << endl;

    return 0;
}

约数之和

问题:求一个数的约数之和

前置知识:算数基本定理
任意一个整数都能被分解为如下形式:
\(n=p^{k_{1}}_{1}×p^{k_{2}}_{2}×....×p^{k_{t}}_{t}\)其中p为质数。

如果n被分解成上面的形式,约数之和显然是
\((p_1^0 + p_1^1 + ... + p_1^{k1}) ×...× (p_t^0 + p_t^1 + + p_tt^{kt})\)

应用:求n个数乘积的约数之和

给定n个正整数,请你输出这些数的乘积的约数之和
思路:
分别求出每一个的,然后最终集中带入公式即可。
代码:

#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>

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 p : primes)
    {
        LL a = p.first, b = p.second;
        LL t = 1;
        while (b -- ) t = (t * a + 1) % mod;
        res = res * t % mod;
    }

    cout << res << endl;

    return 0;
}

求是X的约数的个数(数的值可重复)

题意: 共有 N 个整数 A1,A2,…,AN,对于每一个数 Ai,求其他的数中有多少个是它的约数。
思路: 对于每个数判断其他数是不是他的倍数。
1291. 轻拍牛头

#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;

typedef pair<int, int> pii;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f3f;

int n;
int a[N];
int cnt[N],st[N];
void slove()
{
    cin>>n;
    for(int i=1;i<=n;i++){
      cin>>a[i];
      st[a[i]]++;
    } 
    for(int i=1;i<=N;i++){
      if(!st[i])  continue;
      cnt[i]+=st[i]-1;
      for(int j=i+i;j<=N;j+=i){
        if(st[j])
        cnt[j]+=st[i];
      }
    }
    for(int i=1;i<=n;i++){
      cout<<cnt[a[i]]<<endl;
    }
}

signed main()
{
  // int t;
  // cin >> t;
  // while (t--)
  slove();

  return 0;
}

求n!的约数个数

1294. 樱花
题意
通过化简可以得到题意是求:$(n!)^2 $ 的约数个数
思路:
看代码吧

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define int long long 
const int N =1e6+10;
int mod=1e9+7;
int n;
int primes[N],cnt;
bool st[N];
void getprimes(){
    for(int i=2;i<=n;i++){
        if(!st[i]) primes[cnt++]=i;
        for(int j=0;primes[j]*i<=n;j++){
            st[primes[j]*i]=true;
            if(i%primes[j]==0) break;
        }
    }
}
signed main()
{
    cin>>n;
    int ans=1;
    getprimes();
    // cout<<ans<<endl;
    for(int i=0;i<cnt;i++){
        int p=primes[i];
        int s=0;
        for(int j=n;j;j/=p) s+=j/p;//求n!的约数个数
        ans=(ans*(s*2+1)%mod)%mod;//求n!的平方的约数的个数
    }
    cout<<ans<<endl;
    return 0;
}

198. 反素数

代码:

#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;

typedef pair<int, int> pii;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f3f;
int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
int n;
int maxd, number;
//u: 当前的质因子 
//last:上一个质因子的次方数(也就是这次dfs次方的最大值)
//p:这次dfs之前的 因子的乘积
//s:约数的个数
void dfs(int u, int last, int p, int s)
{
  int k = primes[u];
  if (s > maxd || s == maxd && p < number)
  {
    maxd = s;
    number = p;
  }
  if (u == 9)
    return;
  for (int i = 1; i <= last; i++)
  {
    if (p * primes[u] > n)
      break;
    p *= primes[u];
    dfs(u + 1, i, p, s * (i + 1));
    //s * (i + 1)是因为求的是所有的约数,比如
    //对于18,(3,9),(2,6,18)...都是约数
  }
}
void slove()
{
  cin >> n;
  dfs(0, 30, 1, 1);
  cout << number << endl;
}

signed main()
{
  // int t;
  // cin >> t;
  // while (t--)
  slove();

  return 0;
}

posted @ 2022-07-21 22:00  kingwzun  阅读(106)  评论(0编辑  收藏  举报