Prime sieving and Integer blocking
一、Prime number sieve method
1.埃氏筛O(nloglogn)
从 2 开始,2是质数,那么2的倍数:4、6、8、10、12、14、16… 肯定不是质数
3是质数,那么3的倍数:6、9、12、15、18、21….. 肯定不是质数
4已经被筛去了(即被置为false),不是质数,那么4的倍数肯定被它的因子筛过,所以4不用看,跳过
… …
没有被筛去的一定是质数,因为它没有被比它小的任何一个数筛去,说明它不是任何一个数的倍数,所以一定是质数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <iostream> using namespace std; #include <cstring> const int N = 1e5; bool isPrime[N + 5]; int primes[N + 5]; int cnt = 0;
void aiPrime() { memset(isPrime,true,sizeof(isPrime)); for (int i = 2;i <= N / i;i++) { if (isPrime[i]) { for (int j = i * i; j <= N; j += i) { isPrime[j] = false; } } } for (int i = 2; i <= N; i++) { if(isPrime[i]) primes[cnt++] = i; } } int main() { aiPrime(); cout << cnt << endl; return 0; }
|
2.线性筛
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn =1000010; bool is_pri[maxn]; int pri[maxn]; int cnt;
void init(int n) { memset(is_pri, true, sizeof(is_pri)); is_pri[1] = false; cnt = 0; for (int i = 2; i <= n; i++) { if (is_pri[i]) pri[++cnt] = i; for (int j = 1; j <= cnt && i * pri[j] <= n; j++) { is_pri[i * pri[j]] = false; if (i % pri[j] == 0)break; } } }
|
3.区间筛
给两个数字,求中的所有素数。
为了防止输出过大和防止打表,
给定,,输出这些素数的异或和。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned int u64; const int N =10000100; bool is_pri[N]; int pri[N/5]; int cnt; ll l,r; bool vis[N]; u64 ans = 0,a,b; void init(int n) { memset(is_pri, true, sizeof(is_pri)); is_pri[1] = false; cnt = 0; for (int i = 2; i <= n; i++) { if (is_pri[i]) pri[++cnt] = i; for (int j = 1; j <= cnt && i * pri[j] <= n; j++) { is_pri[i * pri[j]] = false; if (i % pri[j] == 0)break; } } }
int main() { init(N); cin>>l>>r>>a>>b; for(int i = 1;i<=cnt;i++) { int p = pri[i]; for(ll x = r/p*p;x>=l&&x>p;x-=p) vis[x-l] = 1;
} for(ll i = max(2ll,l);i<=r;i++) { if(!vis[i-l]) { ans = ans^(a*(u64)i+b); } } cout<<ans<<endl; return 0; }
|
4.大素数的判断miller_rabin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include<iostream> using namespace std;
typedef long long ll; ll ksm(ll a,ll b,ll mod) { ll ans = 1,base = a; while(b>0) { if(b&1) { ans *= base; ans %= mod; } base *= base; base%=mod; b>>=1; } return ans; }
bool witness(ll a,ll n) { ll u = n-1; int t = 0; while((u&1)==0)u>>=1,t++; ll x1,x2; x1 = ksm(a,u,n); for(int i = 1;i<=t;i++) { x2 = ksm(x1,2,n); if(x2==1&&x1!=1&&x1!=n-1)return true; x1 = x2; } if(x1!=1)return true; return false; }
int miller_rabin(ll n,ll s) { if(n<2)return 0; if(n==2)return 1; if(n%2==0)return 0; for(int i = 0;i<s&&i<n;i++) { ll a = rand()%(n-1)+1; if(witness(a,n))return 0; } return 1; }
int main() { ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int m; while(cin>>m) { int cnt = 0; for(int i = 1;i<=m;i++) { ll n; cin>>n; int s = 50; cnt += miller_rabin(n,s); } cout<<cnt<<"\n"; } return 0; }
|
补充1:单个素数判断
1.试除法
1 2 3 4 5 6 7 8
| inline bool is_prime(int x){ if(x < 2) return false; for(register int i = 2; i * i <= x; ++ i) if(x % i == 0) return false; return true; }
|
2.法
1 2 3 4 5 6 7
| bool isPrime(ll n){ if(n == 2 || n == 3 || n == 5)return 1; if(n % 2 == 0 || n % 3 == 0 || n % 5 == 0 || n == 1) return 0; ll c = 7, a[8] = {4,2,4,2,4,6,2,6}; while(c * c <= n) for(auto i : a){if(n % c == 0)return 0; c += i;} return 1; }
|
3.预处理法
考虑预处理出以内的素数,其中为以内的素数个数。
4.Miller-Rabin 判定法
补充2:素数的性质
- 质数的个数为
- 范围内互质的数最多个。
- 若一个数是一个合数,必然存在个因子,假设,则。因此对于任意一个合数,必然存在一个小于等于的质因子。
- 若,且是合数,则一定存在,使得能整除,其中。
例题:质数距离(筛素数)
题意:给定两个整数 L 和 U,你需要在闭区间 [L,U] 内找到距离最接近的两个相邻质数 C1 和 C2(即 C2−C1 是最小的),如果存在相同距离的其他相邻质数对,则输出第一对。
同时,你还需要找到距离最远的两个相邻质数 D1 和 D2(即 D1−D2 是最大的),如果存在相同距离的其他相邻质数对,则输出第一对。
思路:利用性质3,4。
步骤:
- 用筛法求出区间内的素数。
- 对于内的每一个素数,将区间内所有的倍数全部筛掉。经过了这样的筛选后,区间内剩下的数就都是质数了。
- 注意:要筛掉内所有的倍数,我们只需要找到的的最小倍数:,只后每次即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| #include<bits/stdc++.h> using namespace std; const int N=1e6+5,INF=0x3f3f3f3f; int prime[N],cnt; bool st[N]; void init(int n) { cnt=0; memset(st,0,sizeof st); for(int i=2;i<=n;i++) { if(!st[i]) prime[cnt++]=i; for(int j=0;prime[j]*i<=n;j++) { st[prime[j]*i]=true; if(i%prime[j]==0) break; } } } int main() { int l,r; while(cin>>l>>r) { init(50000); memset(st,0,sizeof st); for(int i=0;i<cnt;i++) { LL p=prime[i]; for(LL j=max(2*p,(l+p-1)/p*p);j<=r;j+=p) st[j-l]=true; } cnt=0; for(int i=0;i<=r-l;i++) if(!st[i]&&i+l>1) prime[cnt++]=i+l; if(cnt<2) puts("There are no adjacent primes."); else { int maxp=1,minp=1; for(int i=2;i<cnt;i++) { int d=prime[i]-prime[i-1]; if(prime[maxp]-prime[maxp-1]<d) maxp=i; if(prime[minp]-prime[minp-1]>d) minp=i; } printf("%d,%d are closest, %d,%d are most distant.\n", prime[minp-1],prime[minp],prime[maxp-1],prime[maxp]); } } return 0; }
|
二、Integer_block
整数分块核心代码
1 2 3 4 5 6
| for(ll l = 1;l<=n;l++) { ll d = n/l,r = n/d; l = r; }
|
eg1.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
#include<bits/stdc++.h> using namespace std; typedef unsigned long long u64;
u64 n,sum;
int main() { cin>>n; for(u64 l = 1;l<=n;l++) { u64 d = n/l,r = n/d; sum += (n-l*d+n-r*d)*(r-l+1)/2; l = r; } cout<<sum%(1ull<<60)<<endl; return 0; }
|