题目描述
我们将素数从小到大依次书写,可以得到一个字符串"23571113⋯",已知一个数码d(0≤d≤9),求字符串在区间[L,R]之间的多少个d?
输入
第一行是一个整数T(1≤T≤10000),表示样例的个数。 每个样例是一行, 为3个整数,区间L,R,(1≤L≤R≤1000000)和数码d。 区间从1开始计数。
输出
每行输出一个样例的结果。
样例输入
2 1 8 1 1 8 4
样例输出
3 0
大体思路:
1.既然是素数一次连接组成的字符串,我们首先要筛选出素数
2.将筛出的素数变为字符串进行处理
3.对于每一个小的素数字符串,都考虑它的每一位对前缀和的贡献。对于一个前缀和,我们定义qzh[i][j]表示前i个字符中有几个j
4.输出只需要输出qzh[R][d] - qzh[L-1][d]即可
具体实现:
对于第一步,使用欧拉筛,代码如下。注:欧拉筛的基本原理详见欧拉筛。
int isprime[5000001];//为1表示这个数不是质数
int prime[5000001];//表示第i个质数是多少
int cnt;//表示筛选出了多少个质数
int qzh[1000001][10]; //表示前i个字符中有多少个d
void euler(){
isprime[1] = 1;
for(int i=2;i<=5000001;i++){
if(!isprime[i]) prime[++cnt] = i; //如果是质数,质数个数+1,第cnt个质数是i
for(int j=1;j<=cnt && i*prime[j]<=5000001;j++){
isprime[i*prime[j]] = 1;//质数的倍数肯定是合数
if(i%prime[j] == 0) break;
}
}
}
对于第二步和第三步,将每一个数字变为字符串,使用一个函数叫做itoa(),这个函数能把数字转化为字符串,格式为itoa(数字,将数字存入的字符串,数字的进制),其余的处理代码,前缀和代码如下:
void connect(){
int cnt=1;//表示到了前几个数了
for(int i=1;i<=1000000;i++){
if(cnt==1000001) break;
char what[1000];//小素数字符串
itoa(prime[i],what,10);//将第i个质数按照10进制转化为字符串what中
for(int j=0;j<strlen(what);j++){
for(int k=0;k<=9;k++){
if(what[j]-'0' == k) qzh[cnt][k] = qzh[cnt-1][k]+1;
else qzh[cnt][k] = qzh[cnt-1][k];//前缀和处理
}
if(cnt==1000000) break;
else cnt++;
}
}
}
最后一步,输出答案即可
euler();
connect();
int T;
scanf("%d",&T);
while(T--){
int L,R,d;
scanf("%d%d%d",&L,&R,&d);
printf("%d\n",qzh[R][d]-qzh[L-1][d]);
}