今天我们来学习一些关于素数和线性筛选法的知识,这类问题在ACM-ICPC中常常遇到,所以很有必要学好它。
首先,来看素数筛选的一个题。
题目:http://codeforces.com/problemset/problem/114/E
题意:给定区间,在这个区间里有多少个素数,使得成立,其中。
分析:由费马平方和定理知道,一个奇素数能表示为两个数的平方和,那么这个奇素数一定是型的。那么
只需要先筛选出所有素数,然后一个一个判断即可,但是这个区间可能很大。普通的素数筛选法是用bool数
组,占一个字节空间,这样很耗费内存,实际上在C++中有一个神器叫做bitset,它是以位为单位的。
代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <bitset>
using namespace std;
const int N = 300000005;
bitset<N> prime;
void Work(int l,int r)
{
prime.set();
for(int i=3; i*i<=r; i+=2)
{
if(prime[i])
{
for(int j=i*i; j<=r; j += i<<1)
prime[j] = false;
}
}
int cnt = (l <=2 && 2 <= r);
for(int i=5; i<=r; i+=4)
if(i >= l && prime[i]) cnt++;
cout<<cnt<<endl;
}
int main()
{
int l, r;
cin>>l>>r;
Work(l,r);
return 0;
}
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1999
题意:设是正整数的所有真因子之和,如果对于任何的,都不等于,那么称为不可模数。
输入一个正整数,判断它是否是不可模数。
分析:可以先用线性筛选法求出的每个数的真因子之和,然后再预处理。
代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
const int N = 1000005;
int sum[N];
bool ok[1005];
void Init()
{
memset(ok,0,sizeof(ok));
for(int i=1;i<N;i++)
for(int j=i+i;j<N;j+=i)
sum[j] += i;
for(int i=1;i<N;i++)
if(sum[i] < 1005)
ok[sum[i]] = 1;
}
int main()
{
Init();
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
if(ok[n]) puts("no");
else puts("yes");
}
return 0;
}
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3823
题意:给定两个数和,找一个最小的,使得和是两个相邻的素数,其中。
代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <bitset>
using namespace std;
const int N = 20000005;
bitset<N> prime;
int p[N];
int cnt;
bool ok[155];
void isprime()
{
cnt = 0;
prime.set();
for(int i=2; i<N; i++)
{
if(prime[i])
{
p[cnt++] = i;
for(int j=i+i; j<N; j+=i)
prime[j] = false;
}
}
}
int main()
{
isprime();
memset(ok,0,sizeof(ok));
for(int i=1;i<cnt;i++)
{
if(p[i] - p[i-1] <= 150)
ok[p[i] - p[i-1]] = 1;
}
int T, a, b, t = 1;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&a,&b);
if(a > b) swap(a, b);
printf("Case %d: ",t++);
if(!ok[b - a] || a == b)
{
puts("-1");
continue;
}
int k = -1;
for(int i=1; i<cnt; i++)
{
if(p[i] - p[i-1] == b - a && p[i-1] >= a)
{
k = i;
break;
}
}
if(k == -1) puts("-1");
else printf("%d\n",p[k] - b);
}
return 0;
}
题目:http://acm.hdu.edu.cn/showproblem.php?pid=2136
题意:给定一个数,求它的最大素因子在素数表中排列第几。
分析:由于输入很多,所以用筛选法预处理。
代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
const int N = 1000005;
int Rank[N];
void Init()
{
int cnt = 1;
for(int i=2;i<N;i++)
{
if(Rank[i]) continue;
for(int j=i;j<N;j+=i)
Rank[j] = cnt;
cnt++;
}
}
int main()
{
int n;
Init();
while(scanf("%d",&n)!=EOF)
printf("%d\n",Rank[n]);
return 0;
}
题目:http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=3349
题意:给定个正整数,范围均在以内,对于每一个数都找出剩下的数中有多少个是它的倍数。
代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
const int N = 1000005;
int cnt[N];
int a[N];
int ans[N];
void Work(int n)
{
int max = 0;
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
cnt[a[i]]++;
if(max < a[i])
max = a[i];
}
for(int i=1; i<=max; i++)
{
if(cnt[i])
{
for(int j=i+i; j<=max; j+=i)
ans[j] += cnt[i];
}
}
for(int i=1; i<=n; i++)
{
if(cnt[a[i]] > 1) printf("%d\n",ans[a[i]] + cnt[a[i]] - 1);
else printf("%d\n",ans[a[i]]);
}
}
int main()
{
int n;
scanf("%d",&n);
Work(n);
return 0;
}