2018 ACM-ICPC 中国大学生程序设计竞赛线上赛-B:goldbach

比赛时花了不到一个小时的时间过了三道水题,然后看到了目前过题数第四多的B题。
题意很简单,给定任意一个在[3,2^63-1]范围内的数,将它表示成两个素数的和。
数字这么大,当然要用素数测试算法啦,比如miller-rabin。
然后花了四个小时调这题,数组都开到了2e6,结果到最后都没过。
不服气,回寝继续做。在群里看到有人说只用查到1e4就好了,而且确实是用这个算法。
那么到底是怎么回事呢?大概是因为测试用到的是一个随机数,算对的可能性可能只有3/4,然后我把测试次数从一次换成了5次就过了,顺便打了个1e5的素数表。
无话可说。还是我太菜了。
那个素数检测部分的代码是我刚才上网随便找的,忘了是谁的了,不过还是要感谢一下作者。
题目链接:
https://nanti.jisuanke.com/t/25985
放上代码回馈网友:

#include <bits/stdc++.h>
#define null NULL
#define PI acos(-1)
#define eps 1e-9
#define INF 0x7fffffff
#define debug(...) fprintf(stderr,__VA_ARGS__),fflush(stderr)

using namespace std;
typedef unsigned long long ll;

ll mod_mul(ll a, ll b, ll n) {
    ll res = 0;
    while(b) {
        if(b&1)    res = (res + a) % n;
        a = (a + a) % n;
        b >>= 1;
    }
    return res;
}

ll mod_exp(ll a, ll b, ll n) {
    ll res = 1;
    while(b) {
        if(b&1)    res = mod_mul(res, a, n);
        a = mod_mul(a, a, n);
        b >>= 1;
    }
    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) || !(n%3) || !(n%5) || !(n%7) || !(n%11))    return false;

    ll x, pre, u;
    int i, j, k = 0,S=5;
    u = n - 1;    //要求x^u % n

    while(!(u&1)) {    //如果u为偶数则u右移,用k记录移位数
        k++; u >>= 1;
    }

    srand((ll)time(0));
    for(i = 0; i < S; ++i) {    //进行S次测试
        x = rand()%(n-2) + 2;    //在[2, n)中取随机数
        if((x%n) == 0)    continue;

        x = mod_exp(x, u, n);    //先计算(x^u) % n,
        pre = x;
        for(j = 0; j < k; ++j) {    //把移位减掉的量补上,并在这地方加上二次探测
            x = mod_mul(x, x, n);
            if(x == 1 && pre != 1 && pre != n-1)    return false;    //二次探测定理,这里如果x = 1则pre 必须等于 1,或则 n-1否则可以判断不是素数
            pre = x;
        }
        if(x != 1)    return false;    //费马小定理
    }
    return true;
}

const ll maxn=1e5;
ll arr[maxn+5];
int prime[maxn+5];

int main(){
    int t=1;scanf("%d",&t);
    int cnt=0;
    for(ll i=0;i<=maxn;++i)
        prime[i]=1;
    for(ll i=2,j=maxn/2;i<=j;++i){
        if(prime[i]){
            arr[cnt++]=i;
            for(ll j=i+i;j<=maxn;j+=i)
                prime[j]=0;
        }
    }
    while(t--){
//        clock_t z=clock();
//        cout<<"Case #"<<++cnt<<": ";
        ll x;scanf("%llu",&x);
        for(int i=0;i<cnt&&arr[i]<x;++i){
            if(miller_rabin(x-arr[i])){
//                cout<<arr[i]<<' '<<x-arr[i]<<endl;
                printf("%llu %llu\n",arr[i],x-arr[i]);
                break;
            }
        }
//        debug("Total Time: %.8f\n", (double)(clock() - z) / CLOCKS_PER_SEC);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值