数论在蓝桥杯上考的不多,但是这不能否定它的重要性。
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);
}
}
}