【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
",pp,qq);
59     }
60     return 0;
61 }
62
Code1 快
 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
",tp,tq);
51     }
52     return 0;
53 }
Code2 慢

而关于线性筛素数,我有2种方法。第一种是每得到一个素数,就让它乘1、2、3...,得到的数标记为不是素数;第二种是最常见的,也是比较快的,每个数都与已得到的素数相乘,得到的数也标记为不是素数,但要小心:当这个数是当前枚举的素数的倍数时,就要跳出循环了。而语句的顺序在理论上为什么在后面,我就不清楚了...O.O

原文地址:https://www.cnblogs.com/konjak/p/6013713.html