ZOJ - 3980 17171771 大素数判定

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3980
这道题是CCPC2017秦皇岛热身赛的一道原题,当时认为可以大表出来,结果没打成。后来比赛结束后想到可以用Miller-Rabin算法来处理素数。

MR测试其实就是利用了费马小定理 ap11(modp) a p − 1 ≡ 1 ( m o d p ) ,a,p互质,p是质数,那么我们随便找个底数a判断一下就行,然而这个定理是充分不必要的,有可能出现伪素数或者Carmichael数,那么我们还需要一个定理:如果p是素数,x是小于p的正整数,且 x2modp=1 x 2 m o d p = 1 ,那么要么x=1,要么x=p-1
以上证明请参照http://www.matrix67.com/blog/archives/234

这道题首先构造出两个数字组成的多位数,像是22223333这样的,然后让next_permutation() do their job ,得出所有的数拿MR测试一check就行,然而乘法会爆long long要注意,不要用加法代替取模,会超时。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <set>
#include <cmath>
#include <ctime>
#define rd(x) (rand()%(x))
using namespace std;
typedef unsigned long long ll;
ll mod_mul(ll x, ll y, ll mod) // 乘法防止溢出, 如果p * p不爆ll的话可以直接乘; O(1)乘法或者转化成二进制加法
{
    return (x * y - (ll)(x / (long double)mod * y + 1e-3) * mod + mod) % mod;
}

long long pow_mod(long long a, long long b, long long n) {
    long long res = 1;
    while(b) {
        if(b & 1)
            res = mod_mul(res, a, n);
        a = mod_mul(a, a, n);
        b >>= 1;
    }
    return res;
}
bool test(ll n,ll a,ll d)
{
    if(n==2) return true;
    if(n==a) return false;
    if(!(n&1)) return false;
    while(!(d&1)) d>>=1;
    ll t = pow_mod(a,d,n);
    while(d!=n-1&&t!=n-1&&t!=1){
        t = mod_mul(t,t,n);//下面介绍防止溢出的办法,对应数据量为10^18次方;
        d<<=1;
    }
    return t == n-1||(d&1)==1;//要么t能变成n-1,要么一开始t就等于1
}

bool isprime(ll n)
{
    int a[] = {2,3,5,233,331};//或者自己生成[2,N-1]以内的随机数rand()%(n-2)+2
    for(int i = 0; i < 5; ++i){
        if(n==a[i]) return true;
        if(!test(n,a[i],n-1)) return false;
    }
    return true;
}
vector<ll> res;
set<ll> ans;
void dfs(int *a,int len)
{
    do
    {
        ll temp = 0;
        if(a[0] == 0)
            continue;
        for(int i = 0; i<len; i++)
        {
            temp = temp*10 + a[i];
        }
        res.push_back(temp);

    }
    while(next_permutation(a,a+len));
}
int a[20];
int main()
{

    for(int i = 0; i<=9; i++)
    {
        for(int j = i+1; j<=9; j++)
        {
            for(int k = 1; k<=8; k++)
            {
                for(int l = 0; l<k; l++)
                {
                    a[l] = i;
                    a[l+k] = j;
                }
                dfs(a,k*2);
            }
        }
    }
    for(int i =0;i<(int)res.size();i++)
    {
        //cout<<res[i]<<endl;
        if(isprime(res[i]))
            ans.insert(res[i]);
    }
    //cout<<ans.size()<<endl;
    for(set<ll>::iterator it = ans.begin();it != ans.end() ;it++)
    {
        printf("%lld\n",(*it));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值