备战蓝桥杯---数论基础刷题1

数论在蓝桥杯上考的不多,但是这不能否定它的重要性。

1.简单的GCD的应用:

分析一下,由等差数列的性质,个数=(an-a1)/d+1,其中an与a1是固定的,因此我们就是让dmax,我们先排一下序,d就是相邻两个数差的gcd,(注意特判0的情况),同时为了方便求,我们让它与a[0]做差即可(答案显然是对的,可以用欧几里得想一想)

下面是AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],n;
int gcd(int a,int b){
    return b? gcd(b,a%b):a;
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    sort(a,a+n);
    int d=0;
    for(int i=1;i<n;i++) d=gcd(d,a[i]-a[0]);
    if(d) cout<<(a[n-1]-a[0])/d+1;
    else cout<<n;
}

2.算数基本定理的应用:

首先我们假设X由a1个质数p1以及a2个质数p2组成,那么我们第一个一定是选一个p1/p2,那么下一个呢?我们要让倍数尽量的小,假如我们乘合数,那么它一定可以分成跟小的质数,假如质数不存在就不选,否则就乘质数,因此我们可以得到一个结论:每次乘p1\p2,因此长度就是a1+a2。

至于个数,就是(a1+a2)!/(a1!*a2!)(高中排列的消序操作)。

这样我们就可以用欧拉筛愉快的AC了:

#include<bits/stdc++.h>
using namespace std;
const int N=(1<<20)+10;
int primes[N],cnt,x;
bool st[N];
int minp[N];
void pp(int n){
    for(int i=2;i<=n;i++){
        if(!st[i]) minp[i]=i,primes[cnt++]=i;
        for(int j=0;primes[j]*i<=n;j++){
            st[primes[j]*i]=1;//删的一定是合数
            minp[primes[j]*i]=primes[j];
            if(i%primes[j]==0) break;//prime[j]一定小于i的最小质因子,保证删合数一定是用其最小质因子,也保证每一个合数一定被删
        }
    }
}
int main(){
    pp(N-1);
    int fact[30],sum[N];
    while(scanf("%d",&x)!=-1){
        int k=0;
        int tot=0;
        while(x>1){
            int p=minp[x];
            fact[k]=p,sum[k]=0;
            while(x%p==0){
                x/=p;
                tot++;
                sum[k]++;
            }
            k++;
        }
        long long ans=1;
        for(int i=1;i<=tot;i++) ans*=i;
        for(int i=0;i<k;i++){
        for(int j=1;j<=sum[i];j++){
            ans/=j;
        }
    }
    cout<<tot<<" "<<ans<<endl;
    }
    
}

3.DFS+约数定理+剪枝:

首先对于一个数,它的正约数之和为(1+p1+p1^2+...)(1+p2+p2^2...)(.....)=s

我们发现这样的数其实很少,于是我们考虑DFS+剪枝:

我们每一轮DFS去枚举p以及幂,若整除则枚举下一层,那么如何剪枝?

我们不妨特判(把表达式分一下类),我们发现当a1==1时S=1+Pi,即S-1一定是质数,否则的话一定是偶数(提一个p).

假如S-1为质数,我们就可以得到答案了。

否则,S-1=(1+pi)(1+pj+..)(..)或(1+pi+pi^2)....

对于第一种pi*pj<pi^2,因此我们只要枚举到根号S即可。

这样子就巧妙地从枚举p从2---S到了根号S。

下面是AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N=50000;
int primes[N],cnt,x,len,ans[N];
bool st[N];
int minp[N];
void pp(int n){
    for(int i=2;i<=n;i++){
        if(!st[i]) minp[i]=i,primes[cnt++]=i;
        for(int j=0;primes[j]*i<=n;j++){
            st[primes[j]*i]=1;//删的一定是合数
            minp[primes[j]*i]=primes[j];
            if(i%primes[j]==0) break;//prime[j]一定小于i的最小质因子,保证删合数一定是用其最小质因子,也保证每一个合数一定被删
        }
    }
}
bool check(int x){
    if(x<N) return !st[x];
    for(int i=0;primes[i]<=x/primes[i];i++){
        if(x%primes[i]==0) return 0;
    }
    return 1;
}
void dfs(int last,int po,int s){
    if(s==1){//完成
        ans[len++]=po;
        return;
    }
    if(s-1>(last<0?1:primes[last])&&check(s-1)){
        ans[len++]=po*(s-1);//完成但是不唯一,因此不return
    }
    for(int i=last+1;primes[i]<=s/primes[i];i++){//防止暴int
        int p=primes[i];
        for(int j=1+p,t=p;j<=s;t*=p,j+=t){
            if(s%j==0) dfs(i,po*t,s/j);
        }
    }
    
}
int main(){
    pp(N-1);
    int s;
    while(cin>>s){
        len=0;
        dfs(-1,1,s);//上一次的下标,当前正确数,剩余的数。
        cout<<len<<endl;
        if(len){
            sort(ans,ans+len);
            for(int i=0;i<len;i++){
                printf("%d ",ans[i]);
            }
            cout<<endl;
        }
    }
}

4.扩展欧几里得算法的简单应用:

具体内容已经写过,这里就不介绍了,这里就放几张图当复习(这个感觉更好理解):

x+bd=y(mod n)

-an+bd=y-x,其中n,d固定,y-x也是常数,我们只要看看y-x是否是gcd的倍数即可。

答案是b=b0+k*n/gcd(n,d)即b0mod(n/gcd(n,d))

下面是AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL exgcd(LL a,LL b,LL &x,LL &y){
    if(!b){
        x=1,y=0;
        return a;
    }
    LL d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        LL n,d,x,y,a,b;
        scanf("%lld%lld%lld%lld",&n,&d,&x,&y);
        int gcd=exgcd(n,d,a,b);
        if((y-x)%gcd) cout<<"Impossible"<<endl;
        else{
            b*=(y-x)/gcd;
            n/=gcd;
            printf("%lld\n",(b%n+n)%n);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值