【noi 2.7_413】Calling Extraterrestrial Intelligence Again(算法效率--线性筛素数+二分+测时)
题意:给3个数M,A,B,求两个质数P,Q。使其满足P*Q<=M且A/B<=P/Q<=1,并使P*Q最大。输入若干行以0,0,0结尾。
解法:先线性筛出素数表,再枚举出P,二分出对应的最大的Q,得出最佳答案。
注意——1.第二个的代码是标准的欧拉筛,可求每个数的最小质因数; 2. 像第一个代码二分时通过位运算,使pri[]的下标尽量大来实现,比一般的二分快了很多很多!!一个6ms,一个502ms。具体请见代码。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 #define M 100010 7 #define D 1010 8 9 bool np[M/10]; 10 int pri[M/20]; 11 int cnt; 12 13 int mmin(int x,int y) {return x<y?x:y;} 14 void init_pri() 15 { 16 cnt=0; 17 memset(np,false,sizeof(np)); 18 for (int i=2;i<=M/10;i++) 19 { 20 if (!np[i]) pri[++cnt]=i; 21 for (int j=1;j<=cnt&&i*pri[j]<=M/10;j++) 22 { 23 np[i*pri[j]]=true; 24 if (i%pri[j]==0) break;//不能调到前一句前 25 } 26 } 27 /*2 28 cnt=0; 29 memset(np,false,sizeof(np)); 30 for (int i=2;i<=M/10;i++) 31 { 32 if (np[i]) continue; 33 pri[++cnt]=i; 34 for (int j=2;i*j<=M/10;j++) 35 np[i*j]=true; 36 } 37 */ 38 } 39 int main() 40 { 41 init_pri(); 42 while (1) 43 { 44 int m,x,y; 45 scanf("%d%d%d",&m,&x,&y); 46 if (m+x+y==0) break; 47 int pp,qq; pp=qq=0; 48 for (int i=1;i<=cnt;i++) 49 { 50 int p=pri[i],lim=mmin(y*p/x,m/p); 51 int tmp=i; 52 for (int j=12;j>=0;j--) 53 if (tmp+(1<<j)<=cnt && pri[tmp+(1<<j)]<=lim) tmp+=(1<<j); 54 if (tmp==i && p*pri[tmp]>m) break;//上面的位调整没有一次成功,这时可能就单乘也是不合法的 55 int q=pri[tmp]; 56 if (p*q>pp*qq) pp=p,qq=q; 57 } 58 printf("%d %d\n",pp,qq); 59 } 60 return 0; 61 } 62 快
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 #define M 100000 7 #define N 1000 8 typedef long long LL; 9 10 int pr=0; 11 LL mn_prim[M+10],prim[M+10]; 12 13 void get_prime() 14 { 15 memset(mn_prim,0,sizeof(mn_prim));//最小质因子 16 for (LL i=2;i<=M;i++) 17 { 18 if (!mn_prim[i]) prim[++pr]=i; 19 for (int j=1;j<=pr;j++) 20 { 21 if (prim[j]*i>M) break; 22 mn_prim[prim[j]*i]=prim[j]; 23 if (i%prim[j]==0) break;// 24 } 25 } 26 } 27 int main() 28 { 29 LL m,a,b; 30 get_prime(); 31 while (1) 32 { 33 scanf("%lld%lld%lld",&m,&a,&b); 34 if (!m&&!a&&!b) break; 35 LL tp=0,tq=0; 36 for (int i=1;i<=pr;i++) 37 { 38 LL p,qq=0; 39 p=prim[i]; 40 int l=i,r=pr; 41 while (l<=r) 42 { 43 int mid=(l+r)>>1; 44 LL q=prim[mid]; 45 if (p*q>m || a*q>b*p) r=mid-1;//相乘会超出int范围 46 else qq=q,l=mid+1; 47 } 48 if (p*qq>tp*tq) tp=p,tq=qq; 49 } 50 printf("%lld %lld\n",tp,tq); 51 } 52 return 0; 53 }
而关于线性筛素数,我有2种方法。第一种是每得到一个素数,就让它乘1、2、3...,得到的数标记为不是素数;第二种是最常见的,也是比较快的,每个数都与已得到的素数相乘,得到的数也标记为不是素数,但要小心:当这个数是当前枚举的素数的倍数时,就要跳出循环了。而语句的顺序在理论上为什么在后面,我就不清楚了...O.O