E. Singhal and Numbers(质因数快速分解倍增优化版本)

https://codeforces.com/gym/102767/problem/E


思路比较直接:套用大数质因数分解的板子。

然而我威海的板子T了..没错板子T了。

于是更新板子,去洛谷找了一个优化版本的。

板子源自网络。这道题用时400ms,相当优秀。

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<map>
#include<stdlib.h>
#include<time.h>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
typedef long long ll;
typedef long long LL;
ll n, Max = 0;

inline ll mul(ll a, ll b, ll mod)
{
    ll d = (long double)a / mod * b + 1e-8;	//还必须是long double……double精度不够
    ll r = a * b - d * mod;
    return r < 0 ? r + mod : r;
}
inline ll quickpow(ll a, ll b, ll mod)
{
	ll ret = 1;
	for(; b; b >>= 1, a = mul(a, a, mod))
		if(b & 1) ret = mul(ret, a, mod);
	return ret;
}

inline bool test(ll a, ll d, ll n)
{
	ll t = quickpow(a, d, n);
    if(t == 1) return 1;
	while(d != n - 1 && t != n - 1 && t != 1) t = mul(t, t, n), d <<= 1;
	return t == n - 1;                       //这里就不用判1了,因为只可能在while前出现1的情况
}
int a[] = {2, 3, 5, 7, 11};
inline bool miller_rabin(ll n)
{
	if(n == 1) return 0;
	for(int i = 0; i < 5; ++i)		//先粗筛一遍
	{
		if(n == a[i]) return 1;
		if(!(n % a[i])) return 0;
	}
	ll d = n - 1;
	while(!(d & 1)) d >>= 1;
	for(int i = 1; i <= 5; ++i)		//搞五遍
	{
		ll a = rand() % (n - 2) + 2;//x属于[2, n - 1]
		if(!test(a, d, n)) return 0;
	}
	return 1;
}

inline ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
inline ll f(ll x, ll a, ll mod) {return (mul(x, x, mod) + a) % mod;}
const int M = (1 << 7) - 1;			//我也不知道为啥M是这个数……
ll pollard_rho(ll n)					//倍增版!减少gcd调用次数。(好像不用判环?)
{
	for(int i = 0; i < 5; ++i) if(n % a[i] == 0) return a[i];
    ll x = rand(), y = x, t = 1, a = rand() % (n - 2) + 2;
    for(int k = 2;; k <<= 1, y = x)
	{
		ll q = 1;
        for(int i = 1; i <= k; ++i)
		{
            x = f(x, a, n);
            q = mul(q, abs(x - y), n);
//            if(i >= M)	//不等价!
            if(!(i & M))	//超过了2 ^ 7,再用gcd
			{
                t = gcd(q, n);
                if(t > 1) break;	//找到了
            }
        }
        if(t > 1 || (t = gcd(q, n)) > 1) break;	//之所以再求一遍,是处理刚开始k < M的情况
    }
    return t;
}
LL factor[100],total=0;
inline void find(ll x)
{
	if(x == 1 || x <= Max) return;
	if(miller_rabin(x)) {factor[++total]=x; return;}
	ll p = x;
	while(p == x) p = pollard_rho(x);
	while(x % p == 0) x /= p;
	find(p); find(x);
}

int main(){
    long long x,m,i,t;
    scanf("%lld",&t);
    while (t--){
        scanf("%lld",&x);
        LL A,B,C;
        scanf("%lld%lld%lld",&A,&B,&C);
        if (miller_rabin(x)){
            printf("%lld\n",C+x);
        }
        else{///X是合数
            memset(factor,0,sizeof(factor));
            total=0;
            find(x);
            sort(factor+1,factor+total+1);
            LL Primemin=factor[1];///最小质因子
            LL Primemax=factor[total];///最大质因子

            LL sum1=A+x/Primemax;
            LL sum2=0;
            LL k=x/Primemin;///最大因数
            if(miller_rabin(k)){
                sum2=A+x/k;
            }
            else{
                sum2=B+x/k;
            }
            LL sum3=C+x;
            printf("%lld\n",min(sum1,min(sum2,sum3)));
        }
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值