数论初步<最大公约,最小共倍,唯一分解定理,素数筛选,扩展欧几里德,取mod运算>

本文深入探讨了数论中的几个难点问题,包括最大公约数、最小公倍数的计算方法,利用唯一分解定理解决实际问题,并给出了具体实例的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

刚看完一点点数论,感慨万千,数论真的是够难啊!!!

1、最大公约。

int gcd(int a,int b){
return b==0?a:gcd(b,a%b);}
2、最小共倍。

c=a*b/gcd(a,b) 或者a/gcd(a,b)*b注意区别,第一种有可能越界。

3、唯一分解定理。

对于任意一个大于1的自然数X2均可写为质数的积,而且这些素因子按大小排列之后,写法仅有一种方式。

例题:除法表达式。给出一个这样的除法表达式:X 1 / X 2 / X 3 / …/ X k ,其中X i 是正整数。除法

表达式应当按照从左到右的顺序求和,例如,表达式1/2/1/2的值为1/4。但可以在表达式中
嵌入括号以改变计算顺序,例如,表达式(1/2)/(1/2)的值为1。
输入X 1 ,  X 2 ,  …, X k ,判断是否可以通过添加括号,使表达式的值为整
数。K≤10000,X i ≤10 9 。

thinking:可以将这个表达式化为X1*(X2*X3*X4...)/X2 

,想想为什么。

然后思路1、大数相乘,把上面的数字都乘起来除X2,判断是否为整数,可得正解,但有些麻烦。

2、把每个X都由唯一分解定理得到为许多有着各种指数的素数的乘积,然后判断X2的每个素数的指数,如果均小于分子相对应的指数,则可以得到整数。

#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
bool vis[5000000];
int qq[555555];
int cc[555555];
int d[555555];
int gg[555555];
int gcd(int a,int b){
    return b=0?a:gcd(b,a%b);
}
int get_prime(int n){    //素数打表
    memset(vis,0,sizeof(vis));
    int m=sqrt(n+0.5);
    for(int i=2;i<=m;i++)if(!vis[i])
        for(int j=i*i;j<=n;j+=i)vis[j]=1;
        int k,i;
        for(k=0,i=2;i<=n;i++){
            if(!vis[i]){
                qq[k++]=i;
          //      cout <<i << endl;
            }
        }
        return k;
}
void get_weiyi(int n){       //唯一分解的素数指数打表
    int j=0;
    while(cc[n]!=1){
         int  x=0;
            while(cc[n]%qq[j]==0){
            cc[n]=cc[n]/qq[j]; //也就是能整除这个素数
                x++;
        }
        d[j]+=x;
        j++;
       }
}

int main(){
    freopen("in.txt","r",stdin);
    int a,b,i,j,n,m;
    memset(vis,0,sizeof(vis));
    memset(qq,0,sizeof(qq));
    memset(cc,0,sizeof(cc));
    memset(d,0,sizeof(d));
    cin >> n;
    int maxn=0;
    for(i=0;i<n;i++){
        cin >> cc[i];
        if(maxn<cc[i])       //找出最大的数,然后素数打表到这个数
            maxn=cc[i];
        }
    int k=get_prime(maxn);
        int x;
        get_weiyi(0);
       for(i=2;i<n;i++){        //然后得到他们的指数。
        get_weiyi(i);
       }
       j=0;
       while(cc[1]!=1){
         int  x=0;
            while(cc[1]%qq[j]==0){
            cc[1]=cc[1]/qq[j]; //也就是能整除这个素数
                x++;
        }
        if(x<=d[j])cout << qq[j] <<"符合"<<endl;
        else cout << qq[j] << "无法整除"<< endl;
        j++;
       }
       //for(i=0;i<k;i++)
      //  cout << d[i] << "\t" << qq[i] << endl;

    return 0;
}

3、还是由唯一分解定理,循环用X2除各个分子的与X2的最大公约数,最终如果X2为1则可以得到整数。

#include <iostream>
#include <math.h>
#include <string.h>
using namespace std;
bool vis[5555555];
int qq[555555];
int c1[666666];
int c2[666666];
int main(){
    int n,i,j,k,m;
    cin >> n;
    m=sqrt(n+0.5);
    for(i=2;i<=m;i++)if(!vis[i])
        for(j=i*i;j<=n;j+=i)vis[j]=1;
        for(j=0,i=2;i<=n;i++)
            if(!vis[i])
                qq[j++]=i;
        for(i=0;i<j;i++)
                cout << qq[i] << endl;

}

素数打表模版,返回的k是素数的数量。

int get_prime(int n){    //素数打表
    memset(vis,0,sizeof(vis));
    int m=sqrt(n+0.5);
    for(int i=2;i<=m;i++)if(!vis[i])
        for(int j=i*i;j<=n;j+=i)vis[j]=1;
        int k,i;
        for(k=0,i=2;i<=n;i++){
            if(!vis[i]){
                qq[k++]=i;
          //      cout <<i << endl;
            }
        }
        return k;
}
唯一分解模版 d里面存的是qq素数中各个素数的指数

void get_weiyi(int n){       //唯一分解的素数指数打表
    int j=0;
    while(cc[n]!=1){
         int  x=0;
            while(cc[n]%qq[j]==0){
            cc[n]=cc[n]/qq[j]; //也就是能整除这个素数
                x++;
        }
        d[j]+=x;
        j++;
       }
}
(筛选法+容斥)
例子,找出N内的无平方因子,无平方因子是指对于n,任意k>1,n都无法整除k^2。

thinking:可以把i循环所有从2到根号n的数i^2的所有倍数都打上标记,意思把所有平方因子都选出来,剩下的就为非平方因子。

因为m=sqrt(n+0.5)。 m<=n的根号,所以筛选从2--m的所有数的平方的倍数即可,

代码:

#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
bool vis[55555555];
int main(){ //.总是运行错误
    //freopen("in.txt","r",stdin);
    int a,b,i,j,n,m;
    cin >> n;
    m=sqrt(n+0.5);
    for(i=2;i<=m;i++)if(!vis[i])
        for(j=i*i;j<=n;j+=i*i)vis[j]=1;   //每次增加的是i的平方的倍数
    for(i=2;i<=n;i++)
        if(!vis[i])cout <<i <<endl;
    return 0;
}
扩展殴几里德算法:

用于线性方程ax+by=c

1、用gcd(a,b,d,x,y)找到x与y是否有一对整数解,若c/d为整数那么就有整数解。

且其一个解为x0=x*c/d,y0=y*c/d。通解为(x0+b/d,y0-a/d)。这时d=gcd(a,b)这个gcd是指最大公约数。

代码:

#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
void gcd(int a,int b,int &d,int &x,int &y){ //扩展欧几里德算法
    if(!b){d=a;x=1;y=0;}
    else{
        gcd(b,a%b,d,y,x);y-=x*(a/b);
    }
}
int main(){ //.总是运行错误
    int a,b,x,y,d,c;
    cin >> a >> b >> c;
    gcd(a,b,d,x,y);     //得出的x,y的解为ax+by=gcd(a,b) 这里的gcd指最大公约数
    //通解为(x0+k*(a/gcd(a,b)),y0-k*(b/gcd(a,b))),其中x0,y0为其的一个解
    if(c%d==0){//只是其中一个解
        cout << "x=" << c/d*x << "\ty=" << c/d*y << endl;
        cout << "x的通解为=k*" << b/d <<"+"<< c/d*x << "   y的通解=-k*" << a/d <<"+"<<c/d*y<< endl;
    }else{
        cout << "无整数解" << endl;
    }
    return 0;
}
取mod问题;

有公式:(a+b)%n=(a%n+b%n)%n.
(a-b)%n=((a%n-b%n)+n)%n  因为有可能a>b,但是其取mod后再减可能会为负数。

a*b%n=((a%n)*(b%n))%n   对于乘法可能左边的最终结果是int型,但右边最后一次%n可能会溢出,所以需要long long 来储存。

乘法取模模版:

用字符串储存,然后a=(a*10+b[i]-'0')%c;便可以做到

#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
char qq[2000];
int main(){ 
    int i,t,n,m,length;
    cin >> qq >> m;   //求大数qq%m的值。
    length=strlen(qq);
    for(i=0;i<length;i++){
        t=t*10+qq[i]-'0';      //有可能最终取mod结果为int型,但是中间乘的时候溢出了,所以应该设置t为long long
        t%=m;
    }
    cout << t << endl;
    return 0;
}

幂取模与快幂运算模版:

可以像乘法一样,int ans=1。

for(i=0;i<k;i++)ans=ans*n%m.    表示有k个n相乘取mod.

这个方法的复杂度为O(N),有个快一点的办法是:

#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
int pow_mod(int qq,int m,int n){ //注意越界问题就好了。
    if(m==0)return 1;
    int ans=pow_mod(qq,m/2,n);
    ans=ans*ans%n;       //这里需要注意*的是返回的值ans,而不是qq!!。
    if(m%2==1)ans=ans*qq%n;
    return ans;
}
int pow(int qq,int m){
    if(m==0)return 1;
    int ans=pow(qq,m/2);
    ans=ans*ans;
    if(m%2==1)ans=ans*qq;
    return ans;
}
int main(){
    int i,t,n,m,qq,length;
    cin >> qq >> m >> n;   //qq的m次方,对n取mod
    cout << pow_mod(qq,m,n) << endl;
    cout << pow(qq,m) << endl;
    return 0;
}

例题Uva11582

题意是 给f(0)=0,f(1)=1,有f(i)=f(i-1)+f(i-2),给定a,b,c三个数,求f(a的b次方)对n取mod的值,

thinking:

无法就是巨大的斐波那契数,由于是取mod,所以必定有周期,注意大指数的所取的方式,然后每次算斐波那契数列的时候也取mod。

#define ll undesing long long  意思是定义long long为不带符号的ll 也就是其的范围比之前*2.也就是1*10^20,long long的范围为9*10^19

代码:

#include <iostream>
#include <string.h>
#include<cstdio>
const int N=1e6+100;
#define ll unsigned long long
using namespace std;
int mod,F[N];
ll n,a,b;
int find1(int n){
    int i=3;
    while(true){
        F[i]=F[i-1]+F[i-2];
        F[i]%=n;
        if(F[i]==1&&F[i-1]==1){
                return i-2;}
        i++;
    }
}
int pow_mod(ll a,ll b){
    if(b==0)return 1;
    long long ans=pow_mod(a,b/2);
    ans=(ans%mod)*(ans%mod)%mod;
    if(b%2==1)ans=ans*(a%mod)%mod;
        return ans;
}
int main(){      
    freopen("in.txt","r",stdin);
    memset(F,0,sizeof(F));
    int T;
    F[0]=0;
    F[1]=1;
    F[2]=1;
    cin >> T;
    int tt;
    while(T--){
    cin >> a >> b>> n;
    mod=find1(n);
      tt=pow_mod(a,b);
    cout << F[tt] <<"\t"<< mod<<endl;
    }
    return 0;
}













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值