今天我们来学习一些关于素数和线性筛选法的知识,这类问题在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; }