2020CCPC威海站 D ABC Conjecture(pollard rho 大数质因数分解)

在这里插入图片描述
题意:给定你一个数c(c < 1e18),问你能否找到一个a和b使得 a + b = c,并且使得他们的乘积 a * b * c的所有不同的素因子的乘积小于c。

思路:给定一个c,我们先把c做质因数分解,然后从他的质因数中选中部分质因数或者他们的幂次构成a,然后我们考虑如何让c-a的质因子能最大程度的尽量和他们相同。

也就是说假如给定c = p1^x1 * p2x2 * p3^ x3 …pn ^ xm,我们如果选择a = p1^x1 * p2^x2 * p3^ x3 …pn ^ (xm-1),那么我们就可以知道b就应该 = p1^x1 * p2x2 * p3^ x3 …pn^(xm-1) * (xm-1),我们可以看到 无论如何 这个xm-1对乘积的贡献一定是比xm小的,所以即使这样选择多abc多出了一个xm-1大小的因子,但是他一定不会使得p1 * p2 * …pn*(xm-1)这个值大于c。

所以我们可以得到结论对于给定的c加入他是个素数,那么a和b是一定找不出来的,如果不是个素数,我们对c进行质因数分解,假如的它有任意至少一个质因子的幂次大于1,那么对于这样的c,a和b是可以找到的,否则也是找不到。

然后对1在特判一下即可。

代码:

/*---------------------------------------------
 POJ 1811 
 最后更新:2019/8/11
 说明:该代码包含了取随机值函数rand()以及求最小值函数min()
 注:适用范围是2^61,至少要保证中间结果不会溢出long long
 (中间结果最多是2*n)
 ----------------------------------------------*/
#include <bits/stdc++.h>

typedef long long ll;
/*---------------------------------------------
 利用 Miller-Rabin进行素性测试
 ----------------------------------------------*/
int testnum[] = {2,7,61,3,5,11,13,19};
ll fmul(ll a,ll b,ll p){
    /*返回a * b % p*/
    a %= p,b %= p;          //防止超出精度
    ll res = 0;
    while(b){
        if(b&1) res += a, res %= p;
        a <<= 1;            //a = a*2
        if(a >= p) a %= p;  
        b >>= 1;            //b = b/2
    }
    return res;
}

ll qpow(ll a,ll b,ll p){
    /*返回a^b % p*/
    ll res = 1;
    while(b){
        if(b&1) res = fmul(res,a,p);
        a = fmul(a,a,p);
        b >>= 1;
    }
    return res;
}

bool isPrime(ll n){
    /*Miller-Rabin判定x是否为素数*/
    if(n == 2) return true;
    if(n < 2 || n%2 == 0) return false;
    ll d = n-1, a, x, y;int t = 0;
    while((d&1) == 0) d >>= 1,t++;
    for(int i = 0;i < 7;i++){
        a = testnum[i];
        if(n == a) return true;
        x = qpow(a,d,n);
        for(int j = 0;j < t;j++){
            y = fmul(x,x,n);
            if(y == 1 && x != 1 && x != n-1) return false;
            x = y;
        }
        if(x != 1) return false;
    }
    return true;
}
/*---------------------------------------------
 利用 pollard rho 算法进行质因数分解 
 ----------------------------------------------*/

ll factors[110];    //用来存放被分解的因数(无序)
int tot = 0;        //因子个数
ll gcd(ll a,ll b){
    /* 返回a和b的最大公约数 */
    if(a == 0) return 1;
    if(a < 0) return gcd(-a,b);
    while(b){
        ll t = a%b;
        a = b; b = t;
    }
    return a;
}
ll pollard_rho(ll x,ll c){
    /* 返回 x 的一个因子或 x 本身 */
    ll i = 1,k = 2;
    ll tx = rand()%x;
    ll y = tx;
    while(true){
        i++;
        tx = (fmul(tx,tx,x)+c)%x;
        ll d = gcd(y-tx,x);
        if(d != 1 && d != x) return d;
        if(y == tx) return x;       //寻找失败
        if(i == k) y = tx, k += k;
    }
}

void findFac(ll n){
    /* 对 n 进行质因数分解 */
    if(isPrime(n)){
        factors[++tot] = n;
        return ;
    }
    ll p = n;
    /* 通过pollard_rho算法找到 n 的一个因子 p */
    while(p >= n) p = pollard_rho(p,rand()%(n-1)+1);
    findFac(p);     //递归分解
    findFac(n/p);
}

std::set<ll>ss;

int main(){
    int t;ll n,ans;
    scanf("%d",&t);
    while(t--){
        scanf("%lld",&n);
        if(n == 1){ printf("no\n");continue; }
        else if(isPrime(n)) puts("no");
        else{
            tot = 0; ans = 1e18;
            int flag = 0;
            findFac(n);
            ss.clear();
            for(int i = 1;i <= tot;i++){
                if(ss.count(factors[i])){ flag = 1;break; }
                else ss.insert(factors[i]);
            }
            if(flag) puts("yes");
            else puts("no");
        } 
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值