题目链接:https://www.luogu.org/problemnew/show/P1865
题目描述
区间质数个数
输入输出格式
输入格式:
一行两个整数 询问次数n,范围m
接下来n行,每行两个整数 l,r 表示区间
输出格式:
对于每次询问输出个数 t,如l或r∉[1,m]输出 Crossing the line
输入输出样例
输入样例#1:
2 5
1 3
2 6
输出样例#1:
2
Crossing the line
说明
【数据范围和约定】
对于20%的数据 :
1 ≤ n ≤ 10
1 ≤ m ≤ 10
对于100%的数据 :
1 ≤ n ≤ 1000
1 ≤ m ≤ 1000000
-109 ≤ l ≤ r ≤ 109
1 ≤ t ≤ 1000000
题目分析:
千言万语终归一句话:我还是太弱了
1.最开始没考虑数据范围,想的也很简单。就是每次查询的时候都从l到r进行遍历查询,是素数的话计数加一。殊不知一个测试点能查一千次,数据范围在亿级,遍历成了一个异常庞大的工程。很明显,TLE。弱到想哭
恬不知耻地贴一下错误代码。。。
#include <bits/stdc++.h>
using namespace std;
int shi(int k)
{
if(k==1)return 0;
for(int i=2;i<=sqrt(k);i++)
if(k%i==0)return 0;
return 1;
}
int main()
{
int n,m;
cin>>n>>m;
while(n--)
{
int s=0;
int l,r;
cin>>l>>r;
if(l<1||l>m||r<1||r>m)
cout<<"Crossing the line"<<endl;
else
{
for(int i=l;i<=r;i++)//这里查一次遍历一次,超时的罪魁祸首
if(shi(i))s++;
cout<<s<<endl;
}
}
return 0;
}
2.查了资料。原来可以做预处理啊(我还是少见多怪了)原理如下:
用前缀和遍历从1到m的所有数值。每遍历一个数值 i,用数组保存从1到 i 的素数个数。这样,遍历一次就行了。
(1)求是否为素数的函数:
int shi(int k)
{
if(k==1)return 0;
for(int i=2;i<=sqrt(k);i++)
if(k%i==0)return 0;
return 1;
}
(2)遍历 1 到 m 的函数
这里说明一下,要点是从1开始向后数,在当前值 i 的时候,如果 i 是素数,则从 1 到 i 的素数个数为从 1 到 i - 1 的个数加 1 ,反之与之前的个数相等。
void pia(int p[],int m)
{
for(int i=1;i<=m;i++)
if(shi(i))p[i]=p[i-1]+1;
else
p[i]=p[i-1];
}
3.这样的话对于每次查询的时候,可以直接调用数组 p ,输出 p[r]-p[l-1] 的值即可。
AC代码:
#include <bits/stdc++.h>
using namespace std;
int m,n;
int shi(int k)
{
if(k==1)return 0;
for(int i=2;i<=sqrt(k);i++)
if(k%i==0)return 0;
return 1;
}
void pia(int p[],int m)
{
for(int i=1;i<=m;i++)
if(shi(i))p[i]=p[i-1]+1;
else
p[i]=p[i-1];
}
int main()
{
int *p;
cin>>n>>m;
p=(int *)malloc(sizeof(int)*(m+1));//开数组
memset(p,0,sizeof(p));
pia(p,m);//预处理
while(n--)//查询
{
int s=0;
int l,r;
cin>>l>>r;
if(l<1||l>m||r<1||r>m)
cout<<"Crossing the line"<<endl;
else
cout<<p[r]-p[l-1]<<endl;
}
free(p);
return 0;
}
现在好了,祖国山河一片绿了,舒心。
总结:
有的时候要学会预处理。。。