题目分析
难度:普及+/提高
暴力40分做法: 从L到R进行枚举,然后判断是否为素数!
但这样显然是不对的,要结合筛法来解决此题,虽然L,R的范围是整个int的取值,但是两者的差值为1e6,可以从此突破!
预备知识 (解决一个问题:求出不大于n的素数的个数)
朴素筛法:时间复杂度 O(n根号n)
//普通朴素算法 求质数 ( n根号n的时间复杂度 )
void Or_Prime(int n)
{
for(int i=2;i<=n;i++)
{
int flag = 0;
for(int j=2;j<=sqrt(i);j++)
{
if(i%j==0)
{
flag = 1;
break;
}
}
if(flag==0) Prime[++cnt] = i;
}
}
埃拉托斯特尼筛法 时间复杂度: O(nloglogn)
要得到自然数n以内的全部素数,必须把不大于的所有素数的倍数剔除,剩下的就是素数。
给出要筛数值的范围n,找出以内的素数。先用2去筛,即把2留下,把2的倍数剔除掉;再用下一个质数,也就是3筛,把3留下,把3的倍数剔除掉;接下去用下一个质数5筛,把5留下,把5的倍数剔除掉;不断重复下去…。
bool isPrime[n]; int Prime[n];
void Er_Prime(int n)
{
for(int i=1;i<=n;i++)
isPrime[i] = 1; //初始化所有数均为素数
for(int i=2;i<=n;++i)
{
if(isPrime[i]==1)
{ //如果i是素数
Prime[++cnt] = i; //则用Prime数组存储该素数,cnt++
for(int j = i*i ;j<=n&&j>=1;j+=i) //注意如果i=50000,则j=i*i会int越界,导致j为负数,从而强行终止此程序!!!
{ //枚举质数i的所有倍数,且从 i*i 开始!!!如果是2*i的话,在i==2的时候已经筛过了
isPrime[j] = 0;
}
}
}
}
欧拉线性筛 时间复杂度: O(n)
在埃氏筛的基础上,保证每一个合数只被筛选一次!!!
//欧拉筛法
void Euler_Prime(int n)
{
for(int i=1;i<=n;i++)
isPrime[i] = 1; //同样初始化所有的数均为素数
for(int i=2;i<=n;i++)
{
if(isPrime[i]==1)
Prime[++cnt] = i; //如果i是素数,则用Prime数组储存下来,并且cnt计数++
for(int j=1;j<=cnt&&i*Prime[j]<=n;j++) //枚举所有的素数,筛掉所有素数的倍数
{
isPrime[i*Prime[j]] = 0;
if(i%Prime[j]==0) break; //i在之前已经被筛过了!
}
}
}
有了筛法的基础,那么接下来的思想就是找出所有位于【2,sqrt(R)】 之间的所有素数,然后用这些素数的倍数来筛掉[L,R]之间的所有合数。
假设现在的某一个素数为p,现在要找的值是大于等于L的第一个p的倍数 num
当L>p时,
如果L%p==0,则num=L;
如果p不能整除L,num=ceil(L/p)*p; ceil(L/p)=floor((L+p-1)/p)=(L+p-1)/p
当L<=p,则num = 2*p(因为p是素数,不能从p开始)
注意ceil的返回值类型为double,小心数值溢出!!!
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
bool isPrime[50000+100];
int cnt;
int Prime[10000+10];
//欧拉筛法
void Euler_Prime(int n)
{
for(int i=1;i<=n;i++)
isPrime[i] = 1;
for(int i=2;i<=n;i++)
{
if(isPrime[i]==1)
Prime[++cnt] = i;
for(int j=1;j<=cnt&&i*Prime[j]<=n;j++) //枚举所有的素数
{
isPrime[i*Prime[j]] = 0;
if(i%Prime[j]==0) break;
}
}
}
bool vis[1000000+10];
int main()
{
ll L,R;
cin>>L>>R;
Euler_Prime(sqrt(R)) ;
for(ll i=1;i<=cnt;i++)
{
ll p = Prime[i]; //取出的 某一个素数
//(ll)ceil((double)L/(double)p) 要对 L/p 进行向上取整获取p的倍数,如果素数p大于L,则从2*p开始(不是p,因为p是素数,不能被筛掉)
for(ll j = max(2,(L+p-1)/p )*p ; j<=R&&j>=2;j+=p )//步长为p,不断寻找p的倍数
vis[j-L+1] = 1; //vis数组的范围为[1,R-L+1] 数组下标平移,防止越界
}
int ans=0;
for(ll i=1;i<=R-L+1;i++)
if(vis[i]==0) ans++;
cout<<ans;
}