5.4 acwing数学知识部分 质数 约数

一、约数

总体思路:

 

1.

872. 最大公约数

给定 nn 对正整数 ai,biai,bi,请你求出每对数的最大公约数。

输入格式

第一行包含整数 nn。

接下来 nn 行,每行包含一个整数对 ai,biai,bi。

输出格式

输出共 nn 行,每行输出一个整数对的最大公约数。

数据范围

1≤n≤1051≤n≤105,
1≤ai,bi≤2×1091≤ai,bi≤2×109

输入样例:

2
3 6
4 6

输出样例:

3
2

 思路:由欧几里得算法,(a,b)和(b,a%b)同余,当b余到最后为0时,无论什么数取0的余数都是它本身。对该递归的证明:

a可以表示成a = kb + r(a,b,k,r皆为正整数,且r

假设d是a,b的一个公约数,记作d|a,d|b,即a和b都可以被d整除。

而r = a - kb,两边同时除以d,r/d=a/d-kb/d,由等式右边可知m=r/d为整数,因此d|r

因此d也是b,a mod b的公约数。

因(a,b)和(b,a mod b)的公约数相等,则其最大公约数也相等,得证。

代码:

#include<iostream>
using namespace std;
#include<vector>
#include<bits/stdc++.h>
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int a,b;
        cin>>a>>b;
        cout<<gcd(a,b)<<endl;
    }
    return 0;
}

2.

869. 试除法求约数

给定 nn 个正整数 aiai,对于每个整数 aiai,请你按照从小到大的顺序输出它的所有约数。

输入格式

第一行包含整数 nn。

接下来 nn 行,每行包含一个整数 aiai。

输出格式

输出共 nn 行,其中第 ii 行输出第 ii 个整数 aiai 的所有约数。

数据范围

1≤n≤1001≤n≤100,
2≤ai≤2×1092≤ai≤2×109

输入样例:

2
6
8

输出样例:

1 2 3 6 
1 2 4 8 

思路:把该数的约数和约数对应的另一个约数都加入即可

#include<iostream>
using namespace std;
#include<bits/stdc++.h>

int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        vector<int>a;
        int m;
        cin>>m;
        for(int i=1;i<=m/i;i++)
        {
            if(m%i==0)
            {
                a.push_back(i);
                if(i!=m/i)a.push_back(m/i);//注意避免重复
            }
        }
        sort(a.begin(),a.end());
        for(auto x:a)cout<<x<<" ";
        cout<<endl;
    }
    return 0;
}

 3.

870. 约数个数

给定 nn 个正整数 aiai,请你输出这些数的乘积的约数个数,答案对 109+7109+7 取模。

输入格式

第一行包含整数 nn。

接下来 nn 行,每行包含一个整数 aiai。

输出格式

输出一个整数,表示所给正整数的乘积的约数个数,答案需对 109+7109+7 取模。

数据范围

1≤n≤1001≤n≤100,
1≤ai≤2×1091≤ai≤2×109

输入样例:

3
2
6
8

输出样例:

12

思路:首先,将每个数的质因数分解的结果放在hash中,has[5]=2,表示5的平方。然后,再根据上图公式计算约数个数和即可。

#include<iostream>
using namespace std;
#include<bits/stdc++.h>
const int N=1e9+7;

int main()
{
    int n;
    cin>>n;
    unordered_map<int,int>hash;
    while(n--)
    {
        int m;
        cin>>m;
        for(int i=2;i<=m/i;i++)
        {
            while(m%i==0)
            {
                m/=i;
                hash[i]++;
            }
            
        }
        if(m>1)hash[m]++;
    }
    long long res=1;
    for(auto x:hash)res=res*(x.second+1)%N;
    cout<<res<<endl;
}

 4.

871. 约数之和

给定 nn 个正整数 aiai,请你输出这些数的乘积的约数之和,答案对 109+7109+7 取模。

输入格式

第一行包含整数 nn。

接下来 nn 行,每行包含一个整数 aiai。

输出格式

输出一个整数,表示所给正整数的乘积的约数之和,答案需对 109+7109+7 取模。

数据范围

1≤n≤1001≤n≤100,
1≤ai≤2×1091≤ai≤2×109

输入样例:

3
2
6
8

输出样例:

252

思路:思路同上,根据公式完成

#include<bits/stdc++.h>
using namespace std;
const int N=1e9+7;
int main()
{
    int n;
    cin>>n;
    unordered_map<int ,int>hash;
    while(n--)//这些步骤都同上
    {
        int m;
        cin>>m;
        for(int i=2;i<=m/i;i++)
        {
            while(m%i==0)
            {
                m/=i;
                hash[i]++;
            }
        }
        if(m>1)hash[m]++;
    }
    long long res=1;
    for(auto x:hash)
    {
        long long t=1;
        for(long long i=1;i<=x.second;i++)
        {
            t=(t*x.first+1)%N;//注意加上括号,这里修改过
        }
        res=res*t%N;
    }
    cout<<res;
}

二、质数

 

 

 5.

866. 试除法判定质数

给定 nn 个正整数 aiai,判定每个数是否是质数。

输入格式

第一行包含整数 nn。

接下来 nn 行,每行包含一个正整数 aiai。

输出格式

共 nn 行,其中第 ii 行输出第 ii 个正整数 aiai 是否为质数,是则输出 Yes,否则输出 No

数据范围

1≤n≤1001≤n≤100,
1≤ai≤231−11≤ai≤231−1

输入样例:

2
2
6

输出样例:

Yes
No

思路:如图

#include<bits/stdc++.h>
using namespace std;
bool pd(int n)
{
    if(n==1)return false;
    if(n==2)return true;
    for(int i=2;i<=n/i;i++)//i<=n/i的判定条件因为i一开始从左向右,当他到达根号n时,右边的对应指针不可能再向左移动,应为已经被i走过了
    {
        if(n%i==0)return false;
        
    }
    return true;
}
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int m;
        cin>>m;
        if(pd(m))cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}

 6.

867. 分解质因数

给定 nn 个正整数 aiai,将每个数分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。

输入格式

第一行包含整数 nn。

接下来 nn 行,每行包含一个正整数 aiai。

输出格式

对于每个正整数 aiai,按照从小到大的顺序输出其分解质因数后,每个质因数的底数和指数,每个底数和指数占一行。

每个正整数的质因数全部输出完毕后,输出一个空行。

数据范围

1≤n≤1001≤n≤100,
2≤ai≤2×1092≤ai≤2×109

输入样例:

2
6
8

输出样例:

2 1
3 1

2 3

思路:如图

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int m;
        cin>>m;
        for(int i=2;i<=m/i;i++)
        {
            if(m%i==0)
            {
                int s=0;
                while(m%i==0)//把i作为质因数除尽,计算后面时就可以除去前面的质因数的影响
                {
                    m/=i;
                    s++;//该质因数有多少个
                }
                cout<<i<<" "<<s<<endl;
            }
        }
        if(m>1)cout<<m<<" "<<1<<endl;//如果m>1,那么说明没有除尽,那么最后一个m就是他的最大质因数
        cout<<endl;
    }
    return 0;
}

 7.

868. 筛质数

给定一个正整数 nn,请你求出 1∼n1∼n 中质数的个数。

输入格式

共一行,包含整数 nn。

输出格式

共一行,包含一个整数,表示 1∼n1∼n 中质数的个数。

数据范围

1≤n≤1061≤n≤106

输入样例:

8

输出样例:

4

第一种解法:暴力解

void get_primes2(){
    for(int i=2;i<=n;i++){

        if(!st[i]) primes[cnt++]=i;//把素数存起来
        for(int j=i;j<=n;j+=i){//不管是合数还是质数,都用来筛掉后面它的倍数
            st[j]=true;
        }
    }
}

 第二种算法:埃式筛法

void get_primes1(){
    for(int i=2;i<=n;i++){
        if(!st[i]){
            primes[cnt++]=i;
            for(int j=i;j<=n;j+=i) st[j]=true;//可以用质数就把所有的合数都筛掉;
        }
    }
}

第三种:线性筛法

思路:如图

#include<iostream>
using namespace std;
#include<vector>
#include<bits/stdc++.h>
const int N=10010
bool st[N];
int ss[N],cnt;
void saichu(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i])ss[cnt++]=i;//先把素数存起来
        for(int j=0;ss[j]<n/i;j++)//即使st[i]不是质,比如4,那么它将进入其中为2*4消除8
        {
            //中心思想是每个合数都将被他最小的质因数消掉
            st[ss[j]*i]=true;//i会慢慢增长,素数也会被存进数组,4会被素数2*i=2消掉,9会被素数3*i=3消掉
            //借鉴的解释:
            //1)当i%primes[j]!=0时,说明此时遍历到的primes[j]不是i的质因子,那么只可能是此时的primes[j]<i的
            //最小质因子,所以primes[j]*i的最小质因子就是primes[j];
            //2)当有i%primes[j]==0时,说明i的最小质因子是primes[j],因此primes[j]*i的最小质因子也就应该是
            //prime[j],之后接着用st[primes[j+1]*i]=true去筛合数时,就不是用最小质因子去更新了,因为i有最小
            //质因子primes[j]<primes[j+1],此时的primes[j+1]不是primes[j+1]*i的最小质因子,此时就应该
            //退出循环,避免之后重复进行筛选。
            if(i%ss[j]==0)break;
            
        }
    }
}
int main()
{
    int n;
    cin>>n;
    saichu(n);
    cout<<cnt;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值