素数性质总结:
- 小于x的素数个数(随着x逐渐增大),与x/lnx近似;
- 素数测试方法,诶拉托色尼筛法:如果n是一个合数,那么n一定有一个不超过sqrt(n)的素因子;6N±1法:对于任何一个自然数,都可以表示为如下形式之一:6N,6N+1,6N+2,6N+3,6N+4,6N+5(N=0,1,2,3...)显然,当N>=1时,只有形如6N+1,6N+5的自然数有可能是素数(代码后面贴上)
- n!的素因子分解中的素数p的幂为 n/p+n/p2+n/p3+......
- 梅森素数:如果m是一个正整数,且2m-1是一个素数,则m必是素数(通过2m-1来判断m是否为素数,很重要的运用);如果m是一个正整数,且m是一个素数,则Mm=2m-1称作第m个梅森数,如果p是一个素数,且Mp=2p-1也是素数,则Mp就称为梅森素数。判断方法有lucas-lehmer判定法【设p是素数,第p个梅森素数为Mp=2p-1,r1=4,对于k>=2,利用rk=r2k-1-2(modMp),0<=rk<Mp,可以得到rk序列,则有Mp为素数,当且仅当rp-1=0(modMp).】和miller素数测试法
miller素数测试法:
一.费马小定理
如果n是素数,且gcd(a,n)==1,那么a(n-1)==1(mod n);
费马小定理只是个必要条件,符合费马小定理而非素数的数叫做Carmichael.
前3个Carmichael数是561,1105,1729。
Carmichael数是非常少的。
在1~100000000范围内的整数中,只有255个Carmichael数。
为此又有二次探测定理,以确保该数为素数:
二.二次探测定理
二次探测定理:如果p是一个素数,0<x<p,则方程x2≡1(mod p)的解为x=1,p-1
题目集合:
hdu 2098 分拆素数和 http://acm.hdu.edu.cn/showproblem.php?pid=2098
6N±1法运用:
/************************************************************** Problem:hdu 2098 User: youmi Language: C++ Result: Accepted Time:0MS Memory:1648K ****************************************************************/ //#pragma comment(linker, "/STACK:1024000000,1024000000") //#include<bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <map> #include <stack> #include <set> #include <sstream> #include <cmath> #include <queue> #include <deque> #include <string> #include <vector> #define zeros(a) memset(a,0,sizeof(a)) #define ones(a) memset(a,-1,sizeof(a)) #define sc(a) scanf("%d",&a) #define sc2(a,b) scanf("%d%d",&a,&b) #define sc3(a,b,c) scanf("%d%d%d",&a,&b,&c) #define scs(a) scanf("%s",a) #define sclld(a) scanf("%I64d",&a) #define pt(a) printf("%d ",a) #define ptlld(a) printf("%I64d ",a) #define rep0(i,n) for(int i=0;i<n;i++) #define rep1(i,n) for(int i=1;i<=n;i++) #define rep_1(i,n) for(int i=n;i>=1;i--) #define rep_0(i,n) for(int i=n-1;i>=0;i--) #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) #define lson (step<<1) #define rson (lson+1) #define esp 1e-6 #define oo 0x3fffffff #define TEST cout<<"*************************"<<endl using namespace std; typedef long long ll; int n; const int maxn=10000+10; int prime[maxn<<2]; bool isprime(int s)//判断s是否为素数 { if(s%2==0) return false; for(int i=3;i*i<=s;i+=2) if(!(s%i)) return false; return true; } void prim() { prime[2]=prime[3]=1; for(int i=3;i<maxn;i+=3) { for(int j=0;j<2;j++) { if(isprime(2*(i+j)-1))//当j=0时为6N-1,当j=1时为6N+1。 prime[2*(i+j)-1]=1; } } } int main() { //freopen("in.txt","r",stdin); prim(); while(~sc(n)) { if(n==0) break; if(n<=4) { printf("0 "); continue; } int ans=(prime[n-2]&&prime[2]); for(int i=3;i<n-i;i+=2) { if(prime[i]&&prime[n-i]) ans++; } pt(ans); } return 0; }
诶拉托色尼筛法:
#include <iostream> #include <stdio.h> //46Ms 244k using namespace std; const int maxn=10000+10; bool prim[maxn]; int num[maxn]; int pnt,n,cnt; void prime() { num[0]=2; pnt=1; for(int i=3;i<=10000;i+=2) { if(!prim[i]) { num[pnt++]=i; for(int j=i*i;j<=10000;j=j+(i*2)) { prim[j]=true; } } } pnt--; } int main() { //freopen("in.txt","r",stdin); prime(); while(scanf("%d",&n)==1&&n) { int p=1; cnt=0; for(int i=num[p];i<n/2;i=num[++p]) { int remainder=n-i; int pos=p; if(remainder%2!=0) { for(int k=i;k<=n&&pos<=pnt;k=num[++pos]) { if(remainder==k) { cnt++; break; } } } } printf("%d ",cnt); } return 0; }
poj 2689 Prime Distance http://poj.org/problem?id=2689 诶拉托色尼筛法的经典题目,两次都是运用这个原理来筛选。第一次把(1<<16)以内的素数全都筛出来,因为R-L<=1000000,所以可以通过(1<<16)以内的素数来筛选L-R之间的素数,而且可以保存在一个大小为1000000的数组里(这其中的奥秘看看代码就好了)。
/************************************************************** Problem:poj 2689 User: youmi Language: C++ Result: Accepted Time:16MS Memory:1580K ****************************************************************/ //#pragma comment(linker, "/STACK:1024000000,1024000000") //#include<bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <map> #include <stack> #include <set> #include <sstream> #include <cmath> #include <queue> #include <deque> #include <string> #include <vector> #define zeros(a) memset(a,0,sizeof(a)) #define ones(a) memset(a,-1,sizeof(a)) #define sc(a) scanf("%d",&a) #define sc2(a,b) scanf("%d%d",&a,&b) #define sc3(a,b,c) scanf("%d%d%d",&a,&b,&c) #define scs(a) scanf("%s",a) #define sclld(a) scanf("%I64d",&a) #define pt(a) printf("%d ",a) #define ptlld(a) printf("%I64d ",a) #define rep0(i,n) for(int i=0;i<n;i++) #define rep1(i,n) for(int i=1;i<=n;i++) #define rep_1(i,n) for(int i=n;i>=1;i--) #define rep_0(i,n) for(int i=n-1;i>=0;i--) #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) #define lson (step<<1) #define rson (lson+1) #define esp 1e-6 #define oo 0x3fffffff #define TEST cout<<"*************************"<<endl using namespace std; typedef long long ll; const int maxn=50010; ll prime[maxn]; bool isprime[maxn*20]; int tot; ll l,r; ll ans[maxn*20]; void prim() { tot=0; memset(isprime,true,sizeof(isprime)); prime[tot++]=2; for(int i=3;i<maxn;i+=2) { if(isprime[i]) { prime[tot++]=i; for(ll j=i;1ll*i*j<1ll*maxn;j+=2) isprime[i*j]=false; } } } void work() { memset(isprime,true,sizeof(isprime));
//以下为两种对L-R之间的数进行筛选的方法 /**<for(int i=0;i<tot;i++) { if(prime[i]>r) break; ll temp=l/prime[i]; while(temp*prime[i]<l||temp<=1)//如果temp=1,也就是temp*prime[i]==prime[i],而prime[i]为素数,所以要特判temp==1的情况 temp++; for(ll j=temp*prime[i];j<=r;j+=prime[i]) if(j>=l) isprime[j-l]=false;//因为R-L<=1000000,所以判断j以后,做差j-l就可以把结果放在一个1000000大小的数组里了 } */ for(int i=0;i<tot;i++) { ll a=(l-1)/prime[i]+1;
if(a==1) a++;//这一步的道理同上 ll b=r/prime[i]; for(ll j=a;j<=b;j++) isprime[j*prime[i]-l]=false; } } void solve() { work(); int cnt=0; ll temp=r-l; for(ll i=0;i<=temp;i++) { if(isprime[i]) ans[cnt++]=l+i; } /**<rep0(i,cnt) printf("%lld ",ans[i]); cout<<endl; */ ll mn,mx; ll ansl1,ansl2,ansr1,ansr2; if(cnt<=1) { printf("There are no adjacent primes. "); return ; } ansl1=ansr1=ans[0]; ansl2=ansr2=ans[1]; mn=mx=ans[1]-ans[0]; for(int i=2;i<cnt;i++) { if(mx<ans[i]-ans[i-1]) { mx=ans[i]-ans[i-1]; ansr1=ans[i-1],ansr2=ans[i]; } if(mn>ans[i]-ans[i-1]) { mn=ans[i]-ans[i-1]; ansl1=ans[i-1],ansl2=ans[i]; } } printf("%lld,%lld are closest, %lld,%lld are most distant. ",ansl1,ansl2,ansr1,ansr2); } int main() { //freopen("in.txt","r",stdin); prim(); while(~scanf("%lld%lld",&l,&r)) { if(l==1) l=2;//因为1比较特殊,所以单独拿出来特判掉,要不然很吃亏的 solve(); } return 0; }
hdu 2138 How many prime numbers http://acm.hdu.edu.cn/showproblem.php?pid=2138 虽然这题比较水,可以直接判断sqrt(n)以内的数是否可以被n整除来判断n是否为素数。但如果用miller素数测试法来做的话还是挺好的一个训练题。不过坑点也蛮多的,我用g++编译相同的代码就tle,而c++就可以刚好过,具体的代码中有标识
/************************************************************** Problem:hdu 2138 User: youmi Language: C++ Result: Accepted Time:764MS Memory:1732K ****************************************************************/ //#pragma comment(linker, "/STACK:1024000000,1024000000") //#include<bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <map> #include <stack> #include <set> #include <sstream> #include <cmath> #include <queue> #include <deque> #include <ctime> #include <string> #include <vector> #define zeros(a) memset(a,0,sizeof(a)) #define ones(a) memset(a,-1,sizeof(a)) #define sc(a) scanf("%d",&a) #define sc2(a,b) scanf("%d%d",&a,&b) #define sc3(a,b,c) scanf("%d%d%d",&a,&b,&c) #define scs(a) scanf("%s",a) #define sclld(a) scanf("%I64d",&a) #define pt(a) printf("%d ",a) #define ptlld(a) printf("%I64d ",a) #define rep0(i,n) for(int i=0;i<n;i++) #define rep1(i,n) for(int i=1;i<=n;i++) #define rep_1(i,n) for(int i=n;i>=1;i--) #define rep_0(i,n) for(int i=n-1;i>=0;i--) #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) #define lson (step<<1) #define rson (lson+1) #define esp 1e-6 #define oo 0x3fffffff #define TEST cout<<"*************************"<<endl using namespace std; typedef long long ll; int N; //下面分别是判15次和判10次的时间差别 const int Times=15;//998MS 1728K //const int Times=10 764MS 1732K ll mod_mul(ll a,ll b,ll mod) { ll res=0; a%=mod; while(b) { if(b&1) res=(res+a)%mod; b>>=1; a=(a<<1)%mod; } return res; } ll mod_exp(ll a,ll b,ll mod) { ll res=1; a%=mod; while(b) { if(b&1) res=mod_mul(res,a,mod); b>>=1; a=mod_mul(a,a,mod); } return res; } bool miller_rabin(ll n) { if(n==2||n==3||n==5||n==7||n==11) return true; if(n==1||n%2==0||n%3==0||n%5==0||n%7==0||n%11==0) return false; int tot=0; ll u=n-1; //要求x^u % n while(!(u&1))//如果u为偶数则u右移,用tot记录移位数,然后可以利用x^2==1来进行tot次二次判定, { tot++;u>>=1; } rep1(i,Times)//进行Times次测试 { ll x=rand()%(n-2)+2;//在[2, n)中取随机数 if(x==n) continue; x=mod_exp(x,u,n);//先计算(x^u) % n ll pre=x; rep0(j,tot)//把移位减掉的量补上,并在这地方加上二次探测 { x=mod_mul(x,x,n); if(x==1&&pre!=1&&pre!=n-1)//二次探测定理,这里如果x = 1则pre 必须等于 1,或则 n-1否则可以判断不是素数 return false; pre=x; } if(x!=1)//费马小定理 return false; } return true; } int main() { //freopen("in.txt","r",stdin); while(~sc(N)) { ll a; int cnt=0; srand(time(NULL));//随机数种子生成器 while(N--) { sclld(a); if(miller_rabin(a)) cnt++; } pt(cnt); } return 0; }
poj 1777 Vivinan's Problem http://poj.org/problem?id=1777 梅森素数性质题,cxlove 大神orz
/************************************************************** Problem:poj 1777 User: youmi Language: C++ Result: Accepted Time:297MS Memory:176K ****************************************************************/ //#pragma comment(linker, "/STACK:1024000000,1024000000") //#include<bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <map> #include <stack> #include <sstream> #include <cmath> #include <queue> #include <string> #include <vector> #define zeros(a) memset(a,0,sizeof(a)) #define ones(a) memset(a,-1,sizeof(a)) #define Max(a,b) (a)>(b)?(a):(b) #define Min(a,b) (a)<(b)?(a):(b) #define sc(a) scanf("%d",&a) #define sc2(a,b) scanf("%d%d",&a,&b) #define rep0(i,n) for(int i=0;i<n;i++) #define rep1(i,n) for(int i=1;i<=n;i++) #define rep_0(i,n) for(int i=n-1;i>=0;i--) #define rep_1(i,n) for(int i=n;i>=1;i--) #define pt(a) printf("%d ",a) #define lson (step<<1) #define rson (lson+1) #define esp 1e-6 #define oo 0x3fffffff #define TEST cout<<"*************************"<<endl using namespace std; typedef long long ll; int n; const int maxn=110; int a[maxn]; int M[8]={3,7,31,127,8191,131071,524287,2147483647}; int p[8]={2,3,5,7,13,17,19,31}; int dp[1<<8]; int work(int temp) { int res=0; for(int i=0;i<8;i++) { if(temp%M[i]==0) { temp/=M[i]; if(temp%M[i]==0) return 0; res|=(1<<i); } } if(temp!=1) return 0; return res; } int cal() { zeros(dp); int ans=0; int res; /**< pt(n); rep0(i,n) printf("%d ",a[i]); cout<<endl;*/ rep0(i,n) { res=0; for(int j=0;j<8;j++) { if(a[i]&(1<<j)) res+=p[j]; } dp[a[i]]=res; //printf("%d %d ",a[i],res); ans=Max(dp[a[i]],ans); } return ans; } void solve() { int ans=cal(); int bit=(1<<8)-1; for(int i=1;i<=bit;i++) { for(int j=1;j<=bit;j++) { if(!(i&j)) { int temp=i|j; dp[temp]=Max(dp[i]+dp[j],dp[temp]); //printf("dp[%d]=%d dp[%d]=%d dp[%d]=%d ",i,dp[i],j,dp[j],temp,dp[temp]); ans=Max(ans,dp[temp]); } } } pt(ans); } int main() { //freopen("in.txt","r",stdin); while(~sc(n)) { rep0(i,n) { sc(a[i]); a[i]=work(a[i]); if(a[i]==0) i--,n--; } if(n==0) { printf("NO "); continue; } solve(); } return 0; }
对素数定理的一些运用:
题目链接:hdu 5778 abs
官方题解:由于y质因数分解式中每个质因数均出现2次,那么y是一个完全平方数,设y=z*z,题目可转换成求z,使得每个质因数出现1次. 我们可以暴力枚举z,检查z是否符合要求,显然当z是质数是符合要求,由素数定理可以得,z的枚举量在logn级别 复杂度 O(sqrt[4]{n}logsqrt[2]{n}4√nlog2√n)

/************************************************************** Problem:hdu 5778 abs User: youmi Language: C++ Result: Accepted Time:31MS Memory:1556K solution:由于y质因数分解式中每个质因数均出现2次,那么y是一个完全平方数,设y=z*z, 题目可转换成求z,使得每个质因数出现1次. 我们可以暴力枚举z,检查z是否符合 要求,显然当z是质数是符合要求,由素数定理可以得,z的枚举量在logn级别 复杂度 O(n^(1/4)log(n^(1/2))) ****************************************************************/ //#pragma comment(linker, "/STACK:1024000000,1024000000") //#include<bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <map> #include <stack> #include <set> #include <sstream> #include <cmath> #include <queue> #include <deque> #include <string> #include <vector> #define zeros(a) memset(a,0,sizeof(a)) #define ones(a) memset(a,-1,sizeof(a)) #define sc(a) scanf("%d",&a) #define sc2(a,b) scanf("%d%d",&a,&b) #define sc3(a,b,c) scanf("%d%d%d",&a,&b,&c) #define scs(a) scanf("%s",a) #define sclld(a) scanf("%I64d",&a) #define pt(a) printf("%d ",a) #define ptlld(a) printf("%I64d ",a) #define rep(i,from,to) for(int i=from;i<=to;i++) #define irep(i,to,from) for(int i=to;i>=from;i--) #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) #define lson (step<<1) #define rson (lson+1) #define eps 1e-6 #define oo ((1ll<<62)-1) #define TEST cout<<"*************************"<<endl const double pi=4*atan(1.0); using namespace std; typedef long long ll; template <class T> inline void read(T &n) { char c; int flag = 1; for (c = getchar(); !(c >= '0' && c <= '9' || c == '-'); c = getchar()); if (c == '-') flag = -1, n = 0; else n = c - '0'; for (c = getchar(); c >= '0' && c <= '9'; c = getchar()) n = n * 10 + c - '0'; n *= flag; } int Pow(int base, ll n, int mo) { if (n == 0) return 1; if (n == 1) return base % mo; int tmp = Pow(base, n >> 1, mo); tmp = (ll)tmp * tmp % mo; if (n & 1) tmp = (ll)tmp * base % mo; return tmp; } //*************************** const int maxn=50010; ll n; bool work(ll x) { for(ll i=2;i*i<=x;i++) { if(x%i==0) { ll temp=x/i; if(temp%i==0) return false; } } return true; } int c[]={0,3,2,1,0,1,2,2,1,0,1}; int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif int T_T; scanf("%d",&T_T); for(int kase=1;kase<=T_T;kase++) { sclld(n); if(n<=10) { printf("%d ",c[n]); continue; } ll tt=(ll)sqrt(n*1.0); ll ans=oo; for(ll i=tt;i>=2;i--) { if(work(i)) { ans=Min(ans,abs(n-i*i)); break; } } for(ll i=tt+1;i<=n;i++) { if(work(i)) { ans=Min(ans,abs(n-i*i)); break; } } printf("%I64d ",ans); } }