Problem
找出区间[l,r]之间由{2,3,5,7}四种数字组成的质数的个数
1≤l≤r≤1015
0≤r−l≤109
Solution
设由{2,3,5,7}四种数字组成的数为A类数,A类数中的质数为B类数,我们要求的是B类数的个数。
由于有条件0≤r−l≤109,那么区间[l,r]内A类数最多有49=262144个,那么最开始我们需要找到第一个大于等于l的A类数,接着我们可以仿照四进制加法来找到下一个这样的A类数,判断一个A类数是不是质数就可以直接用Miller_Rabin。
Code
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<set>
#include<map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long LL;
typedef double db;
struct number{int len,a[20];}L,R;
bool bz[10];
int nxt[10]={2,2,3,5,5,7,7,2,0,0};
int pri[11]={0,2,3,5,7,11,13,17,19,23,29};
bool operator < (number a,number b){
if (a.len!=b.len)return a.len<b.len;
fd(i,a.len,1)
if (a.a[i]!=b.a[i])return a.a[i]<b.a[i];
return 1;
}
void getnum(LL x,number &a){
a.len=0;
while(x){
a.a[++a.len]=x%10;
x/=10;
}
}
void init(number &a){
int w=0;
fd(i,a.len,1)
if (!bz[a.a[i]]){w=i;break;}
if (w){
if (a.a[w]>7){
fo(i,1,w)a.a[i]=0;
a.a[w+1]++;
if (w==a.len)a.a[++a.len]=1;
init(a);
return;
}
a.a[w]=nxt[a.a[w]];
fo(i,1,w-1)a.a[i]=2;
}
}
number getnxt(number a){
int w=a.len+1;
fo(i,1,a.len)
if (a.a[i]!=7){w=i;break;}
a.len=max(a.len,w);
fo(i,1,w)a.a[i]=nxt[a.a[i]];
return a;
}
LL quickmi(LL x,LL tim,LL mo){
LL ans=1;
while(tim){
if (tim%2)ans=ans*x%mo;
x=x*x%mo;
tim/=2;
}
return ans;
}
bool check(LL x,LL p,LL t,LL mo){
x=quickmi(x,p,mo);
if (x==mo-1||x==1)return 1;
fo(i,1,t){
if (x==1)return 0;
x=x*x%mo;
if (x==mo-1)return 1;
}
return 0;
}
bool miller_rabin(number a){
LL x=0;
fd(i,a.len,1)x=x*10+a.a[i];
LL p=x-1;
int t=0;
while(p%2==0){p/=2;t++;}
fo(i,1,10){
if (x==pri[i])return 1;
if (!check(pri[i],p,t,x))return 0;
}
return 1;
}
int main(){
LL l,r;
scanf("%lld%lld",&l,&r);
getnum(l,L);
getnum(r,R);
bz[2]=bz[3]=bz[5]=bz[7]=1;
init(L);
int ans=0;
while(L<R){
if (miller_rabin(L)){
ans++;
fd(i,L.len,1)printf("%d",L.a[i]);putchar('\n');
}
L=getnxt(L);
}
printf("%d\n",ans);
return 0;
}