COJ0700 数学(一)

试题描述
现在有一大堆数,请你对这些数进行检验。
输入
第一行:CAS,代表数据组数(不大于500000),以下CAS行,每行一个数字,保证在64位长整形范围内,并且没有负数。你需要对于每个数字检验是否是质数。
输出
共一行,输出质数的数量,保证是在64位长整形范围内的正数。
输入示例
4 2 3 5 4
输出示例
3
其他说明
数据范围: 保证cas<=100000,保证所有数字均在64位长整形范围内。 请尽量优化你的程序,包括OI优化。 欢迎暴力法不服来辩呦→ →

 首先有这样一个定理:

若p是一个质数,x是一个整数,且x^2 mod p = 1,那么x ≡ ±1 (mod p)。

证明:x^2 mod p = 1       ->       p | x^2 - 1        ->       p | (x - 1)(x + 1),又因为p是质数故x-1与x+1中有一个是p的倍数。

但这个定理的逆定理是个假命题,不过它很少有反例。我们就可以利用逆定理来判定素数了。

设待测数为n,任取一个比n小的正整数a,设 n - 1 = r * 2^s,若n是质数则以下条件至少有一个成立:
1.a^s mod n = 1

2.存在一个整数i满足:0<=i<s且a^(d*(2^i)) mod n = -1

重复以上步骤3至4次即可稳定出解。

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define rep(s,t) for(int i=s;i<=t;i++)
using namespace std;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
typedef long long ll;
ll pow(ll n,ll m,ll p) {
    ll ans=1,t=n;
    while(m) {
        if(m&1) (ans*=t)%=p;
        (t*=t)%=p;m>>=1;
    }
    return ans;
}
int check(ll a,ll n,ll r,ll s) {
    ll ans=pow(a,r,n);if(ans==1) return 0;
    rep(1,s) {
        if(ans==n-1) return 0;
        (ans*=ans)%=n;
    }
    return 1;
}
int isprime(ll n) {
    if(n==2) return 1;
    if(n<=1||!(n&1)) return 0;
    int r=n-1,s=0;
    while(!(r&1)) r>>=1,s++;
    rep(1,3) if(check(rand()%(n-1)+1,n,r,s)) return 0;
    return 1;
}
int main() {
    int T=read(),ans=0;
    while(T--) ans+=isprime(read());
    printf("%d\n",ans);
    return 0;
}
View Code

 

posted @ 2015-07-15 17:16  wzj_is_a_juruo  阅读(176)  评论(0编辑  收藏  举报