居然是一道模板题,院赛时就只知道暴力打表,发现毫无规律,然后暴力预处理,结果肯定TLE。。。。。,学长讲题解时,发现原来是数论中的:
素数筛:
于是赛后去学习了下素数筛,主要有两种方法
一、埃氏筛;二、欧拉筛
思想:对于不超过n的每个非负整数p,删除是p的整数倍的数,当处理完这些数之后,还没被删除的数就是素数。
代码如下:
#include
#define maxn 100
using namespace std;
int vis[maxn+10]; //判断是不是素数
int prime[maxn+10]; //存放素数的数组
int main()
{
memset(vis,0,sizeof(vis));
int m=0;
for(int i=2;i<=maxn;i++)
{
if(vis[i]==0)
prime[m++]=i;
for(int j=i*2;j<=maxn;j+=i)
vis[j]=1;
}
for(int i=0;i<m;i++)
cout<<prime[i]<<endl;
return 0;
}
改进:p可以限定为素数——只需在第二重循环前加一个判断if(vis[i]==0)即可
代码如下:
#include
#define maxn 100
using namespace std;
int vis[maxn+10];
int prime[maxn+10];
int main()
{
memset(vis,0,sizeof(vis));
for(int i=2;i<=maxn;i++)
{
if(vis[i]==0)
{
for(int j=i*i;j<=maxn;j+=i)
vis[j]=1;
}
}
int m=0;
for(int i=2;i<=maxn;i++)
{
if(vis[i]==0)
prime[m++]=i;
}
for(int i=0;i<m;i++)
cout<<prime[i]<<endl;
return 0;
}
思路:因为prime[]数组中是按从小到大顺序存储,当i能整除prime[j],那么i*prime[j+1]这个合数肯定被prime[j]乘以某个数筛掉。(其实我是没搞太明白)
代码如下:
#include
#define maxn 100
using namespace std;
int vis[maxn+10];
int prime[maxn+10];
int main()
{
memset(vis,0,sizeof(vis));
int m=0;
for(int i=2;i<=maxn;i++)
{
if(vis[i]==0)
prime[m++]=i;
for(int j=0;j<m&&prime[j]*i<=maxn;j++)
vis[prime[j]*i]=1;
}
for(int i=0;i<m;i++)
cout<<prime[i]<<endl;
return 0;
}
ps:以上都是求100以内素数的代码样例
XP对数论非常不感冒,有一天,XSW向他提出这样一个问题:现在给定一个函数f(x),设x的所有素因子个数为k(1不是素数,x本身可以算作它的因子),则f(x) = (-1)^k,设S(x) = S(x-1) + x*f(x),其中S(1) = f(1),现在给定一个N,你的目的是要求出S(N)的值。XP学长对于XSW提出的这个问题有些迷茫,所以现在请你来解决这个问题。
输入包含多组测试用例,第一行输入一个T表示测试数据组数,(1<=T<=100)
每组测试数据输入一个N,(1<=N<=10^5)
上述S(N)的值。
1
5
-13
提示
样例中,我们首先可以知道f(1) = 1,f(2) = -1,f(3) = -1,f(4) = -1,f(5) = -1,因此S(5) = 1 - 2 - 3 - 4 - 5 = -13。
于是三种算法AC代码如下:
一:普通埃氏筛
#include
#define maxn 100000
using namespace std;
int vis[maxn+10];
int prime[maxn+10];
int s[maxn+10];
int main()
{
memset(vis,0,sizeof(vis));
int m=0;
for(int i=2;i<=maxn;i++)
{
if(vis[i]==0)
prime[m++]=i;
for(int j=i*2;j<=maxn;j+=i)
{
vis[j]=1;
}
}
//for(int i=0;i<10;i++)
// cout<
//cout<
s[1]=1;
for(int i=2;i<=maxn;i++)
{
int sum=0;
for(int j=0;j<m&&prime[j]<=i/2;j++)
{
if(i%prime[j]==0)
sum++;
}
if(vis[i]==0)
sum++;
if(sum%2==0)
s[i]=s[i-1]+i;
else
s[i]=s[i-1]-i;
}
//for(int i=1;i<=10;i++)
// cout<
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
printf("%d\n",s[n]);
}
return 0;
}
/**
2
3
5
7
11
13
17
19
23
29
1
-1
-4
-8
-13
-7
-14
-22
-31
-21*/
二、改进后的埃氏筛
#include
#define maxn 100000
using namespace std;
int vis[maxn+10];
int prime[maxn+10];
int s[maxn+10];
int main()
{
memset(vis,0,sizeof(vis));
int m=0;
int num=sqrt(maxn+0.5);
for(int i=2;i<=num;i++)
{
if(vis[i]==0)
{
for(int j=i*i;j<=maxn;j+=i)
vis[j]=1;
}
}
for(int i=2;i<=maxn;i++)
{
if(vis[i]==0)
prime[m++]=i;
}
//for(int i=0;i<10;i++)
// cout<
//cout<
s[1]=1;
for(int i=2;i<=maxn;i++)
{
int sum=0;
for(int j=0;j<m&&prime[j]<=i/2;j++)
{
if(i%prime[j]==0)
sum++;
}
if(vis[i]==0)
sum++;
if(sum%2==0)
s[i]=s[i-1]+i;
else
s[i]=s[i-1]-i;
}
//or(int i=1;i<=10;i++)
// cout<
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
printf("%d\n",s[n]);
}
return 0;
}
三、欧拉筛
#include
#define maxn 100000
using namespace std;
int vis[maxn+10];
int prime[maxn+10];
int s[maxn+10];
int main()
{
memset(vis,0,sizeof(vis));
int m=0;
for(int i=2;i<=maxn;i++)
{
if(vis[i]==0)
prime[m++]=i;
for(int j=0;j<m&&prime[j]*i<=maxn;j++)
{
vis[prime[j]*i]=1;
}
}
s[1]=1;
for(int i=2;i<=maxn;i++)
{
int sum=0;
for(int j=0;j<m&&prime[j]<=i/2;j++)
{
if(i%prime[j]==0)
sum++;
}
if(vis[i]==0)
sum++;
if(sum%2==0)
s[i]=s[i-1]+i;
else
s[i]=s[i-1]-i;
}
//for(int i=1;i<=10;i++)
// cout<
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
printf("%d\n",s[n]);
}
return 0;
}
不知道为什么,我写的三份代码居然跑起来时间差不多。。。。。。。