2019 ICPC 徐州区域赛 - E Multiply(Pollard-Rho质因数分解)

题目链接


题目大意

给出一个大小为 n n n的序列 a a a,定义 Z = ∏ i = 1 n ( a i ! ) Z=\prod_{i=1}^n(a_i!) Z=i=1n(ai!);给出 X X X, Y Y Y,若 b = Z ∗ X i b = Z*X^i b=ZXi,求出最大的 i i i使得 b b b Y ! Y! Y!的因数。

解题思路

X X X质因数分解得 p 1 , p 2 , p 3 ⋅ ⋅ ⋅ p n p_1,p_2,p_3···p_n p1,p2,p3pn。对于某质因子 p i p_i pi,设 Z Z Z中有 a a a个, X X X中有 b b b个, Y ! Y! Y!中有 c c c个。则该质因子 p i p_i pi最多对应 ( c − a ) / b (c-a)/b (ca)/b个,再求出所有质因子中最小的那个 i i i,即为答案

关于 n ! n! n!的质因数分解:

考虑 n ! n! n!的所有质因数都不会超过 n n n,显然可用的质因数就是 n n n以内的所有因数。考虑如何求出 n ! n! n!所有的质因数的个数。先分析一个简单的问题,求出 n n n以内质数 p p p的倍数的个数,即 ⌊ n p ⌋ \lfloor \frac{n}{p} \rfloor pn,但是 n n n的阶乘是前 n n n个数相乘,而且对于某个质数,我们只知道它的倍数是没有用的,需要知道每个数上该质数的幂次是多少。而 n n n以内 p p p的倍数设分解为 p k ∗ q p^k*q pkq,显然 k = 1 , 2 , 3 , . . . k=1,2,3,... k=1,2,3,...。那么 ⌊ n p ⌋ \lfloor \frac{n}{p} \rfloor pn求出的只是 k = 1 k=1 k=1的数的个数,还有 k = 2 , 3 , . . . k=2,3,... k=2,3,... p p p的倍数,这时我们如果再将上述结果除以 p p p,即 ⌊ ⌊ n p ⌋ p ⌋ \lfloor \frac{\lfloor \frac{n}{p} \rfloor}{p} \rfloor ppn,也相当于是 ⌊ n p 2 ⌋ \lfloor \frac{n}{p^2} \rfloor p2n,这求出的是 k = 2 k=2 k=2时的倍数个数,以此类推。将所有结果相加,得到的恰好是 n ! n! n!中质数 p p p的个数。

PS:关于Pollard_rho质因数分解的板子,去博客找的板子都是WA,无奈下搜了题解,还请教了学长。后来在整理板子时,特地去计蒜客找了另一道题,又再洛谷找了一道题后,可以去检测一下自己的板子是不是有小瑕疵

计蒜客

洛谷

以下代码参考了KobeDuu的博客,博主还帮我解决了一些疑惑,不胜感谢

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+16;
LL x,y,a[maxn];
struct BigIntegerFactor{
    const static int maxm = 1e6+16;
    LL prime[maxm],p[maxm],fac[maxm],sz,cnt;     //多组输入注意初始化cnt = 0
    inline LL mul(LL a,LL b,LL mod){             //WA了尝试改为__int128或慢速乘
        if(mod <= 1000000000) return a * b % mod;
        return (a*b-(LL)((long double)a/mod*b+1e-8)*mod+mod)%mod;
    }
    void init(int maxn){
        int tot = 0; sz = maxn-1;
        for(int i = 1;i <= sz; ++i) p[i] = i;
        for(int i = 2;i <= sz; ++i){
            if(p[i] == i) prime[tot++] = i;
            for(int j = 0;j<tot&&1ll*i*prime[j]<=sz; ++j){
                p[i*prime[j]] = prime[j];
                if(i%prime[j] == 0) break;
            }
        }
    }
    LL powl(LL a,LL x,LL mod){
        LL res = 1LL;
        while(x){
            if(x&1) res = mul(res,a,mod);
            a = mul(a,a,mod);
            x >>= 1;
        }
        return res;
    }
    bool check(LL a,LL n){                       //二次探测原理检验n
        LL t = 0,u = n-1;
        while(!(u&1)) t++,u >>= 1;
        LL x = powl(a,u,n),xx = 0;
        while(t--){
            xx = mul(x,x,n);
            if(xx==1 && x!=1 && x!=n-1) return false;
            x = xx;
        }
        return xx == 1;
    }
    bool miller(LL n,int k){
        if(n == 2) return true;
        if(n < 2 || !(n&1)) return false;
        if(n <= sz) return p[n] == n;
        for(int i = 0;i <= k; ++i){               //测试k次
            if(!check(rand()%(n-1)+1,n)) return false;
        }
        return true;
    }
    inline LL gcd(LL a,LL b){
        return b == 0 ? a : gcd(b,a%b);
    }
    inline LL Abs(LL x){
        return x < 0 ? -x : x;
    }
    LL Pollard_rho(LL n){                  //基于路径倍增的Pollard_Rho算法
        LL s = 0,t = 0,c = rand()%(n-1)+1,v = 1,ed = 1;
        while(1){
            for(int i = 1; i <= ed; ++i){
                t = (mul(t,t,n) + c) % n; v = mul(v,Abs(t-s),n);
                if(i % 127 == 0){
                    LL d = gcd(v,n);
                    if(d > 1) return d;
                }
            }
            LL d = gcd(v,n); if(d > 1) return d;
            s = t; v = 1; ed <<= 1;           
        }
    }
    void getfactor(LL n){                          //得到所有的质因子(可能有重复的)
        if(n <= sz){
            while(n != 1) fac[cnt++] = p[n],n /= p[n];
            return;
        }
        if(miller(n,6)) fac[cnt++] = n;
        else{
            LL d = n; while(d >= n) d = Pollard_rho(n);
            getfactor(d); getfactor(n/d);
        }
    }
    LL cal(LL n,LL x){                            //计算 n! 中质因子 x 的数量
        LL num = 0;
        while(n){
            num += n/x;
            n = n/x;
        }
        return num;
    }
    LL solve(int n,LL x,LL y){
        map<LL,LL> mp;  LL ans = 4e18;
        cnt = 0; getfactor(x);
        for(int i = 0;i < cnt; ++i) mp[fac[i]]++;
        map<LL,LL>::iterator it = mp.begin();
        while(it != mp.end()){
            LL num = 0;
            for(int i = 1;i <= n; ++i){
                num += cal(a[i],it->first);
            }
            ans = min(ans,(cal(y,it->first)-num)/it->second);
            it++;
        }
        return ans;
    }
}Q;
int main(){
    Q.init(100000);
    int T,n; scanf("%d",&T);
    while(T--){
        scanf("%d %lld %lld",&n,&x,&y);
        for(int i = 1;i <= n; ++i) 
            scanf("%lld",a+i);
        printf("%lld\n",Q.solve(n,x,y));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值