数学知识(数论一)

数学知识(数论一)

质数

在这里插入图片描述

思路:把1~根号n之间的数都找一遍,看看有没有一个数是n的因子,之所以找到根号n,是因为因子都是成对出现的,假设i是n的因子,且i<sqrt(n),那么i必定存在另外一个因子x>sqrt(n),所以只需要找到较小的因子即可

866试除法判定质数

在这里插入图片描述

#include<iostream>
#include<algorithm>

using namespace std;

int n;

bool is(int x){
    
    if(x<2)return false;
    
    for(int i=2;i<=x/i;i++){              //注意这里不建议写i<=sqrt(x)这样会调用sqrt函数,使效率下降,写i*i<=x可能会溢出
        
        if(x%i==0)return false;
        
    }
    
    
    return true;
    
}



int main(){
    
    cin>>n;
    
    while(n--){
        
        int x;
        
        cin>>x;
        
        if(!is(x))cout<<"No"<<endl;
        
        else  cout<<"Yes"<<endl;
        
    }
    
    return 0;
    
}

867分解质因数

  • 思路:
  1. 从2开始试除,x每能整除i时将i“除干净”(因此会将x的合数因子提前除干净,枚举剩下的就是质因子)
  2. 统计i的指数次数,并依次试除直到sqrt(x),如果x仍然大于1,输出最后一个大于sqrt(x)的质因数(有且只有一个)

在这里插入图片描述

#include<iostream>
#include<algorithm>
using namespace std;

int n;

void  divide(int x){
    
    for(int i=2;i<=x/i;i++){
        
        if(x%i==0){
            
            int s=0;                                    //统计该因数的指数
            
            while(x%i==0){
                
                s++;
                
                x/=i;
                
            }
            
            printf("%d %d\n",i,s);
            
        }
        
    }
    
    
    if(x>1)printf("%d %d\n",x,1); //最多只有一个大于sqrt(x)的质数且他的指数是1,(有两个就大于x了)
    
    
}

int main(){
    
    cin>>n;
    
    while(n--){
        
        int x;
        
        cin>>x;
        
        divide(x);
        
        printf("\n");
        
    }
    
    
}

868筛质数

在这里插入图片描述

(一)朴素筛法:

  1. 枚举2~n
  2. 从2开始将它们的倍数(2,4,6...)从st数组里面全部删除
  3. 如果枚举到一个数p它仍然在st里(没有被删除)说明它是个质数(没有1~p-1的因子
  4. 时间复杂度(n/2+n/3+n/4+…n-1/n+n/n)(调和级数)——删2的倍数,3的倍数,4的倍数…
    =nlnn≈nlogn

优化:只需要删除质数的倍数即可

质数定理:1~n中只有n/lnn个质数
时间复杂度:近似为O(n)实际为O(nloglogn)

在这里插入图片描述

//朴素优化
#include <iostream>
#include<algorithm>

using namespace std;

const int N =1e6+10;

int  n;

int cnt;

int st[N],primes[N];

//朴素优化版
void get(int x){
    
    for(int i=2;i<=x;i++){
        
        if(!st[i])primes[cnt++]=i;
        
        for(int j=i+i;j<=x;j+=i)st[j]=true;   //把素数的所倍数删除
        
    }
    
    
}

int main(){
    
    cin>>n;
    
    get(n);
    
    cout<<cnt;
    
    return 0;
    
 }

(二)线性筛法:

  • 保证每个和数都是被他的最小质因子筛掉
    例如:10会被2筛掉而不是被5筛掉,这样保证每个数只会被删除一次,
  • 时间复杂度O(n)
  • n>10^7的时候会比朴素法快一倍,小于10^7的时候差不多

思路:枚举i=2~n
保证每个i都被它的最小质因子筛掉(primes[j]质数数组)
当枚举到ist数组里仍然存在i,说明i是质数,加入primes数组
对于每个i,枚举primes[j]
删除st[primes[j]*i]保证这个被删除的数是被它的最小质因子删除的

在这里插入图片描述

判断如果i%primes[j]==0 说明primes[j]已经枚举到i的最小质因子了,跳出

#include <iostream>
#include<algorithm>

using namespace std;

const int N =1e6+10;

int  n;

int cnt;

int st[N],primes[N];

//线性法
void get(int n){
    
   for(int i=2;i<=n;i++){
       
       if(!st[i])primes[cnt++]=i;
       
       for(int j=0;primes[j]<=n/i;j++){  //删的最大的数(primes[j]*i)不能超过n
           
           st[primes[j]*i]=true;              //每次把primes[j]*i删除
           
           if(i%primes[j]==0)break;          //已经枚举到了i的最小质因子,此时说明i已经被他的最小质因子删除了(i==2的时候pj等于2跳出(此时删除4),i等于4的时候pj等于2跳出(此时删除8),6的时候pj等于2跳出,9的时候pj等于3跳出....)
           
       }
       
   }
    
    
}

int main(){
    
    cin>>n;
    
    get(n);
    
    cout<<cnt;
    
    return 0;
    
 }

约数

所有int范围内,约数最多的一个数大约有1500个约数

在这里插入图片描述

每一个数N可以表示成一个连乘积的形式p1^a1*p2 ^a2…
他的约数d也可以表示成这种形式

每一项p都有0~ai种选法,所以有上面的约数个数公式
从每一项中选一个p组成连乘积,他们的和就是约数之和,有上面的约数之和的公式

在这里插入图片描述

869试除法求约数

在这里插入图片描述

#include<vector>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

vector<int>  primes;

int n;

void  get(int x){
    
    primes.clear();
    
    for(int i=1;i<=x/i;i++){                       //从1枚举到sqrt(n)
        
        if(x%i==0){
            primes.push_back(i);
        
            if(i!=(x/i))primes.push_back(x/i);      //如果另一个约数和i不相等,加入primes
        }
        
    }
    
    sort(primes.begin(),primes.end());
    
    
}


int main(){
    
    cin>>n;
    
    while(n--){
        
       int x;
       
       cin>>x;
     
       get(x);
       
       for(auto prime:primes)cout<<prime<<" ";
       
       cout<<endl;
     
    }
    
    
    
    return 0;
}

870约数个数

在这里插入图片描述
思路:只要将这些数挨个表示成连乘积的形式,保留每一项的底数和指数,最后使指数相乘即可

#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>

using namespace std;

const int N=110,mod=1e9+7;

typedef long long LL;

int n;

int main(){
    
    cin>>n;
    
    unordered_map <int,int> primes;            //无序键值对  unordered_map<key,value>key是底数  value是指数
    
    while(n--){
        
        int x;
        
        cin>>x;
        
        for(int i=2;i<=x/i;i++){                //分解质因数
            
            while(x%i==0){
                
                x=x/i;
                
                primes[i]++;
            }
            
        }
        
        if(x>1)primes[x]++;                  //最后一个可能的质因子
    
        
    }
    
    LL  res=1;
    
    for(auto prime:primes){
        
        res=res*(prime.second+1)%mod;         //每乘一个数都mod,遍历到prime等于0相当于乘1
        
    }
    
    cout<<res;

    
}

871约数之和

在这里插入图片描述

#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>

using namespace std;

const int mod=1e9+7;

typedef long long LL;

int n;

int main(){
    
    cin>>n;
    
    unordered_map <int,int> primes;            //无序键值对  unordered_map<key,value>key是底数  value是指数
    
    while(n--){
        
        int x;
        
        cin>>x;
        
        for(int i=2;i<=x/i;i++){                //分解质因数
            
            while(x%i==0){
                
                x=x/i;
                
                primes[i]++;
            }
            
        }
        
        if(x>1)primes[x]++;                  //最后一个可能的质因子
    
        
    }
    
     LL res=1;
    
    for(auto prime:primes){
        
        int p=prime.first,a=prime.second;
        
        LL s=1;                                 //s=p^a+p^a-1+......p^1+p^0;
        
        for(int i=1;i<=a;i++)
        
        s=(s*p+1)%mod;                          //防止每一项溢出
        
        res=res*s%mod;
    }
    
    
    cout<<res;
    
}

872最大公约数

a与b的最大公约数c也是a%b的公共约数

在这里插入图片描述

#include<iostream>
#include<algorithm>

using namespace std;

int n;

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


int main(){
    
    cin>>n;
    
    while(n--){
        
        int a, b;
        
        cin>>a>>b;
        
        cout<<gcd(a,b);
        
        cout<<endl;
    }
    
    
}
posted @ 2019-11-23 22:20  zzcxxoo  阅读(266)  评论(0)    收藏  举报