题解 | #区间素数求和#

区间素数求和

https://ac.nowcoder.com/acm/problem/206984

埃氏筛法

alt

昨天提交还是只通过90%,今天提交就过了,我也很迷

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int n=1e6+5;
long long vis[n],prime[n];
int main()
{
	/*
	先假设所有的数都是素数
	0和1不是素数,标记出来
	接着,从2开始遍历,所有素数的倍数必然是合数,而所有合数的倍数也必然是合数
	把遍历到的数的倍数都标记出来,那么合数就都被标记了 
	唯一缺陷就是,会存在重复标记,因为一个合数很可能有两个质因子 
	*/
    long long t,l,r,s=0,i,j,cnt=0;
        cin>>t;
    	memset(vis,0,sizeof(vis)); //假设所有的数都是素数
    	vis[0] = vis[1] = 1;  //0 和 1不是素数
    	for (i = 2; i <n; i++)
        	if (!vis[i]) {         //如果i是素数,让i的所有倍数都不是素数
            	prime[cnt++]=i;//存储所有的素数 
            	for (j = i*i; j <n; j += i) 
                	vis[j] = 1;
        	} 
    while(t--){
    	cin>>l>>r;
        s=0;
        for(i=0;prime[i]<=r;i++) 
             if(prime[i]>=l) s+=prime[i];
    	cout<<s<<endl;
	}
 } 
 
 

这是之前的代码,过了90%的那个,好像就只是因为宏常量的原因,,,

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long vis[1000010],prime[1000010];
int main()
{
	/*
	先假设所有的数都是素数
	0和1不是素数,标记出来
	接着,从2开始遍历,所有素数的倍数必然是合数,而所有合数的倍数也必然是合数
	把遍历到的数的倍数都标记出来,那么合数就都被标记了 
	唯一缺陷就是,会存在重复标记,因为一个合数很可能有两个质因子 
	*/
    long long t,l,r,cnt=0,s=0,i,j;
        cin>>t;
    	memset(vis,0,sizeof(vis)); //假设所有的数都是素数
    	vis[0] = vis[1] = 1;  //0 和 1不是素数
    	for (i = 2; i <1000001; i++)
        	if (!vis[i]) {         //如果i是素数,让i的所有倍数都不是素数
            	prime[cnt++]=i;//存储所有的素数 
            	for (j = i*i; j <1000001; j += i) 
                	vis[j] = 1;
        	} 
    while(t--){
    	cin>>l>>r;
        s=0;
        for(i=0;prime[i]<=r;i++) 
             if(prime[i]>=l) s+=prime[i];
    	cout<<s<<endl;
	}
 } 

欧拉筛(到底还是要比埃氏筛法快一些)

alt

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int n=1e6+5;
long long vis[n],prime[n];
int main()
{
    long long t,l,r,s=0,i,j,cnt=0;
        cin>>t;
    	memset(vis,0,sizeof(vis)); //假设所有的数都是素数
    	vis[0] = vis[1] = 1;  //0 和 1不是素数
    	for (i = 2; i <n; i++){//i的初始值必须是2,不能是1
        	if (!vis[i])         //如果i是素数,让i的所有倍数都不是素数
            	prime[cnt++]=i;//存储所有的素数 
            for (int j = 0; j <cnt&& i*prime[j] <= n; j++){
                vis[i*prime[j]] = 1;
                if (i % prime[j] == 0) break;
            }
        } 
    while(t--){
    	cin>>l>>r;
        s=0;
        for(i=0;prime[i]<=r;i++) 
             if(prime[i]>=l) s+=prime[i];
    	cout<<s<<endl;
	}
 } 
 
 

关于for (int j = 0; j <cnt&& iprime[j] <= n; j++) vis[iprime[j]] = 1; 的解释

解释一:
prime[j]是当前已经确定的素数,而i*prime[j]计算的该素数的倍数 

	此前有一点疑惑, i是外层循环的控制变量,有可能轮到这个素数的时候,
i已经>2、3、4、5。。。了,就没办法计算这个素数的 2、3、4、5。。。倍了。

但是其实是这样的:
	如果该素数前有素数,那么其实这两个素数的倍数已经被标记为合数了,作为(比prime[j]小的
素数)的倍数被标记了 

解释二: 

	我们之前做埃氏筛法的优化时就已经知道了,循环标记时j的下标应该从i*i开始,原因见《计算机考
研机试指南 》P96,而在这里,我们得到一个新的素数的时候,prime[j]是通过i赋值给它得到的,
那么其实i*prime[j]的初始值就相当于i*i ,也因此i是不会小于prime[j]的,也即i>=prime[j] 永真

	i如果是素数,那么i=prime[j]的最大值
	i如果是合数,那么i>prime[j]的最大值 

《计算机考研机试指南 》P96 alt

关于 if (i % prime[j] == 0) break;的解释

	j是从小到大的, 那么prime[j]也是从小到大的,prime[j]里面存的都是素数,那么i对prime[j]
求余能除尽,说明prime[j]是i的质因子,那么i对prime[j]求余,遇到的第一个能除尽的数就是i的最
小质因子
菜鸟成长记 文章被收录于专栏

根据自己的学习历程,记录该过程中的各种困惑以及解决困惑的方法

全部评论

相关推荐

微风不断:兄弟,你把四旋翼都做出来了那个挺难的吧
点赞 评论 收藏
分享
hso_:哈哈哈哈哈哈我没offer一样在同一道题开喷了
投递深圳同为数码等公司10个岗位
点赞 评论 收藏
分享
评论
点赞
1
分享
牛客网
牛客企业服务