总纲:
1.筛法求素数
2.欧几里德算法
3.扩展欧几里德
4.最小公倍数
5.斐波那契
6.快速幂
7.唯一分解定理
8.中国剩余定理
9.欧拉函数
1.筛法求素数:
用筛法求素数的基本思想是:把从1开始的、某一范围内的正整数从小到大顺序排列, 1不是素数,首先把它筛掉。剩下的数中选择最小的数是素数,然后去掉它的倍数。依次类推,直到筛至n结束。
注意:最小的素数为2,所以筛时应注意从2开始
过程举例:
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
1不是素数,去掉。剩下的数中2最小,是素数,去掉2的倍数,余下的数是:
3 5 7 9 11 13 15 17 19 21 23 25 27 29
剩下的数中3最小,是素数,去掉3的倍数,如此下去直到所有的数都被筛完,求出的素数为:
2 3 5 7 11 13 17 19 23 29
代码:
1 #include<iostream> 3 #include<cstdio> 5 #include<cmath> 7 using namespace std; 9 int vis[1000]; 11 int main() 13 { 15 int n; 17 cin>>n; 19 for(int i=2;i<=sqrt(n)+1;i++) 21 { 23 if(vis[i]==0) 25 { 27 for(int j=i*2;j<=n;j+=i)//从i*2开始筛,i--2*i由i之前筛去 29 { 31 vis[j]=1; 33 } 35 } 37 } 39 for(int i=2;i<=n;i++) 41 { 43 if(vis[i]==0) 45 { 47 cout<<i<<" "; 49 } 51 } 53 }
1453 统计素数个数 2
时间限制: 1 s
空间限制: 256000 KB
题目等级 : 黄金 Gold
题目描述 Description
判断[a,b]中素数的个数
输入描述 Input Description
输入共1行,a,b两数
输出描述 Output Description
输出共1行,输出素数的个数
样例输入 Sample Input
3 5
样例输出 Sample Output
2
数据范围及提示 Data Size & Hint
对于100%的数据,a,b≤5000,000
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 using namespace std; 5 const int N=5000000; 6 int vis[N]; 7 int main() 8 { 9 int l,r; 10 cin>>l>>r; 11 vis[0]=1; 12 vis[1]=1; 13 for(int i=2;i<=sqrt(r)+1;i++) 14 { 15 if(vis[i]==0) 16 { 17 for(int j=i*2;j<=r;j+=i) 18 { 19 vis[j]=1; 20 } 21 } 22 } 23 int ans=0; 24 for(int i=l;i<=r;i++) 25 { 26 if(vis[i]==0) 27 { 28 ans++; 29 } 30 } 31 cout<<ans; 32 }
——————————————————————————————————
——————————————————————————————————
2.欧几里德辗转相除求最大公约数:
gcd函数的基本性质:
gcd(a,b)=gcd(b,a)=gcd(-a,b)=gcd(|a|,|b|)
辗转相除法举例:
若a<b,则a/b=0(余a);
求(319,377):
∵ 319÷377=0(余319)
∴(319,377)=(377,319);
∵ 377÷319=1(余58)
∴(377,319)=(319,58);
∵ 319÷58=5(余29)
∴ (319,58)=(58,29);
∵ 58÷29=2(余0)
∴ (58,29)= 29;
∴ (319,377)=29。
代码:
#include<iostream> #include<cstdio> using namespace std; int gcd(int x,int y) { return x%y==0?y:gcd(y%x,x); } int main() { int n,m; scanf("%d%d",&n,&m); printf("%d ",gcd(n,m)); } }
——————————————————————————————————
3.扩展欧几里德
扩展欧几里德求解同余方程的解
例解 ax≡1mod(b);
给出a,b;
求x的最小值;
求上述等价于 ax+by=1,x,y,属于Z;
有欧几里德辗转相除可知
gcd(a,b)=gcd(b,a%b); 所以 当b==0是,a就是gcd(a,b)的值; 也就是 此时 gcd(a,b)=a*1+b*0==a; 即原式中x==1,y==0; 设 gdc(a,b)=a*x1+b*y1 gcd(b,a%b=b*x2+a%b*y2 ∵gcd(a,b) =gcd(b,a%b) ∴a*x1+b*y1=b*x2+a%b*y2 化简上式可得 a*x1+b*y1=b*x2+a%b*y2 a*x1+b*y1=b*x2+(a-a/b*b)*y2 a*x1+b*y1=a*y2+b*x2-a/b*b*y2 a*x1+b*y1=a*y2+b*(x2-a/b*y2) 则此时 x1=y2 y1=(x2-a/b*y2)
所以递归式就出来了;
代码:
1 #include <cstdio> 2 int a, b; 3 int exgcd(int a,int b,int &x,int &y) 4 { 5 if(b==0) 6 { 7 x=1,y=0; 8 return a; 9 } 10 int r=exgcd(b,a%b,x,y),tmp; 11 tmp=x,x=y,y=tmp-a/b*y; 12 //x1=y2 ; 13 //y1= x2-a/b*y2; 14 return r; 15 } 16 int main() 17 { 18 scanf("%d%d", &a, &b); 19 int x, y; 20 exgcd(a, b, x, y); 21 while (x < 0) x = x + b;//最小正整数解 22 printf("%d", x); 23 return 0; 24 }
——————————————————————————————————
_____________________________________________________________________
4.最大公约数
N,m最大公约数就是用a*b/gcd(a,b);
代码:
int lcm(int x,int y)
{
return x*y/gcd(x,y);
}
_____________________________________________________________________
5.菲波那切数列
0 1 1 2 3 5 8 13 21 34 55 89 144……
递归的定义为:F[0]=0,F[1]=1,F[2]=1,F[3]=2,F[4]=3……
F[n]=F[n-1]+F[n-2];
斐波那契数列通项公式的证明:
<1>.公式法
求斐波那契的N项的通项公式为:
;
代码:(注意:除ans用long long类型外都用double类型)
用long long类型最多算到第92项;
用int 类型最多算到第46项;
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 using namespace std; 5 const double a=(1+sqrt((double)5))/2; 6 const double b=(1-sqrt((double)5))/2; 7 int main() 8 { 9 long long c; 10 cin>>c; 11 double n=sqrt(5); 12 double m=pow(a,c)-pow(b,c); 13 long long ans=1/n*m; 14 cout<<ans; 15 }
<2>.递归法:
注意:递归适用于层数较小的结果算到第38项时时间差不多到1s;
代码:
1 #include<iostream> 2 3 #include<cstdio> 4 5 using namespace std; 6 7 int FB(int n) 8 9 { 10 11 if(n==1||n==0)return 1;//固定值 12 13 else return FB(n-1)+FB(n-2);//递归公式 14 15 } 16 17 int main() 18 19 { 20 21 int n; 22 23 cin>>n; 24 25 int ans=FB(n); 26 27 cout<<ans; 28 29 } 30 31 <3>.递推法: 32 33 注意:用地推法数组F[]开long long类型最多算到第95项; 34 35 首先另f[1]=0;f[2]=1;//此处根据需要赋f[1],f[2]的初值; 36 37 然后运用公式f[n]=f[n-1]+f[n-2]求得; 38 39 代码: 40 41 #include<iostream> 42 43 #include<cstdio> 44 45 using namespace std; 46 47 const int N=1000; 48 49 long long f[N]; 50 51 int main() 52 53 { 54 55 int n; 56 57 cin>>n; 58 59 f[1]=0; 60 61 f[2]=1; 62 63 for(int i=3;i<=n;i++) 64 65 { 66 67 f[i]=f[i-1]+f[i-2]; 68 69 } 70 71 cout<<f[n]; 72 73 }
例题:
2834 斐波那契数
时间限制: 1 s
空间限制: 128000 KB
题目等级 : 黄金 Gold
题目描述 Description
小X是个聪明的孩子,他记得斐波那契数列f(n)中前1000个数。不过由于学业的压力,他无法记得每一个数在数列中的位置。
他现在知道斐波那契数列中的一个数f(x)模P后的值N(即f(x) mod P=N)以及x可能的最大值M,如果再对于斐波那契数列中每一个数都模P,他想知道这个数可能出现在第几个。不过小X还要做作业呢,这个问题就交给你由编程来解决了。
输入描述 Input Description
一行,共3个整数,第一个数为N,第二个数为P,第三个数为x可能的最大值M,三个数以空格隔开。
输出描述 Output Description
一个整数,满足f(i) mod P = N的最小的i,如果不存在则输出-1。
样例输入 Sample Input
3 7 5
样例输出 Sample Output
4
数据范围及提示 Data Size & Hint
对于20%的数据,保证0<M≤50
对于50%的数据,保证0<M≤100
对于70%的数据,保证0<M≤500
对于100%的数据,保证0<M≤1000,0≤N<P,P为素数且2<P<105。
1 #include<iostream> 2 3 #include<cstdio> 4 5 using namespace std; 6 7 const int N=1000; 8 9 int f[N]; 10 11 int main() 12 13 { 14 15 int n,p,m; 16 17 cin>>n>>p>>m; 18 19 f[1]=1; 20 21 f[2]=1; 22 23 if(n==1){cout<<"1";return 0;}//tepan 24 25 for(int i=3;i<=m;i++) 26 27 { 28 29 f[i]=(f[i-1]+f[i-2])%p; 30 31 if(f[i]==n) 32 33 { 34 35 cout<<i; 36 37 return 0; 38 39 } 40 41 } 42 43 cout<<"-1"; 44 45 return 0; 46 47 }
__________________________________________________________________________________________________________________________________________
5.快速幂
进制转换代码:
1 #include<iostream> 2 3 #include<cstdio> 4 5 using namespace std; 6 7 int a[1000]; 8 9 int main() 10 11 { 12 13 int n; 14 15 cin>>n; 16 17 int j=0; 18 19 int i=n; 20 21 while(i!=0) 22 23 { 24 25 a[++j]=i%2;//2进制 26 27 i/=2;//2进制 28 29 } 30 31 for(int i=j;i>=1;i--) 32 33 { 34 35 cout<<a[i]; 36 37 } 38 39 }
快速幂就是快速算底数的n次幂
求a^n
将n转换为2进制数;从其2进制数的低位到高位将其拆成a^n0*a^n1*a^n2……;
例如2^11;
11的2进制数为1011;
则 2^11=2^3*1+2^2*0+2^1*1+2^0+1;
代码:
1 #include<iostream> 2 3 #include<cstdio> 4 5 using namespace std; 6 7 int pow2(int a,int b) 8 9 { 10 11 int r=1,base=a; 12 13 while(b!=0)//将其转化为2进制数 1位1位列举 14 15 { 16 17 if(ans==1) r*=base;//b%2==1说明此数2进制最后一位不为0; 18 19 base*=base;//此数累乘; 20 21 b/=2;//将其2进制最后一位向前移一; 22 23 //b>>1; 24 25 } 26 27 return r; 28 29 } 30 31 int main() 32 33 { 34 35 int n,m; 36 37 cin>>n>>m; 38 39 cout<<pow2(n,m); 40 41 }
__________________________________________________________________________________________________________________________________________
6.唯一分解定理
存在性证明:
设 M = P1 * P2 * … * Pr = Q1 * Q2 * … * Qs
P1 <= P2 <= ... <= Pr, Q1 <= Q2 <= ... <= Qs。
P1是不等于Q1的,不然两边同时约掉它,我们就得到一个更小的有两种分解方法的数
不妨设P1 < Q1,那么我们用P1替换掉等式最右边中的Q1,得到一个比M更小的数T = P1 * Q2 * Q3 * ... * Qs令M' = M - TM' = (P1 * P2 * ... * Pr) - (P1 * Q2 * ... * Qs) = P1 * (P2 * .. * Pr - Q2 * ... * Qs) …… (1) M' = (Q1 * Q2 * ... * Qs) - (P1 * Q2 * ... * Qs) = (Q1 - P1) * Q2 * ... * Qs ……………… (2) 由于T比M小,因此M'是正整数。从(1)式中我们立即看到,P1是M'的一个质因子。注意到M'比M小,因此它的质因数分解方式应该是唯一的,可知P1也应该出现在表达式(2)中既然P1比所有的Q都要小,因此它不可能恰好是(2)式中的某个Q,于是只可能被包含在因子(Q1-P1)里。但这就意味着,(Q1-P1)/P1除得尽,也就是说Q1/P1-1是一个整数,这样Q1/P1也必须得是整数。我们立即看出,P1必须也是Q1的一个因子,这与Q1是质数矛盾了。这说明,我们最初的假设是错误的。
求解n的分解
代码:
1 #include <iostream> 2 3 #include<cstdio> 4 5 using namespace std; 6 7 int main() 8 9 { 10 11 int i,a[10000],c,n,t; 12 13 scanf("%d",&t); 14 15 while(t--) 16 17 { 18 19 scanf("%d",&n); 20 21 c=0; 22 23 for(i=2;i<=n;i++) 24 25 { 26 27 int ans=n%i; 28 29 while(ans==0) 30 31 { 32 33 a[c++]=i; 34 35 n/=i; 36 37 ans=n%i; 38 39 } 40 41 } 42 43 for(i=0;i<c;i++) 44 45 { 46 47 printf(i==0?"%d":"*%d",a[i]); 48 49 } 50 51 printf(" "); 52 53 } 54 55 return 0; 56 57 }
__________________________________________________________________________________________________________________________________________
7.中国剩余定理
8.欧拉函数