2019 ICPC Asia Xuzhou Regional E. Multiply(唯一分解定理/大数分解)

Alduhmellah and Behlah both like large numbers, lots of numbers and lots of large numbers. They also like to do calculations on those numbers.

One day, Alduhmellah wrote down NN positive integers a_1, a_2,\cdots, a_Na1,a2,⋯,a**N. He decided to make them large by applying factorial to each number, so the numbers became a_1!, a_2!,\cdots, a_N!a1!,a2!,⋯,a**N!. Finally, he multiplied all NN numbers to get a super-large number Z=a_1!\times a_2!\times\cdots\times a_N!Z=a1!×a2!×⋯×a**N!.

A few days later, Behlah met Alduhmellah, and Behlah told Alduhmellah that he came up with another two numbers X,YX,Y. Thus, they started to repeatedly multiply XX to ZZ, generating a sequence b_i=Z\times X^ib**i=Z×X**i, to find out the largest value of ii such that b_ib**i is a factor of Y!Y!.

You, being a friend with Alduhmellah and Behlah, realized that it is such a time-wasting process. Thus, to save time, you decided to write a program to calculate the answer.

Input

The first line contains one positive integer TT (T\leq 8T≤8) --- the number of tests.

The description of each test contains two lines: the first line contains three positive integers N, X, YN,X,Y (N\leq 10^5; 2\leq X,Y\leq 10^{18}N≤105;2≤X,Y≤1018), and the second line contains NN positive integers a_1, a_2,\cdots a_Na1,a2,⋯a**N (a_i \leq 10^{18}; a_1+a_2+\cdots+a_N<Ya**i≤1018;a1+a2+⋯+a**N<Y). Their meanings are explained above.

Output

For each test, output an integer in one line, representing the largest value of ii such that b_ib**i is a factor of Y!Y!.

样例输入复制

2
3 10 10
2 3 4
2 2 10
1 1

样例输出复制

2
8

大意就是求最大的i使得\(Z\times X^i | Y\),其中\(Z = \prod_{i = 1}^n a_i!\)

这个题和某些gcd相关的题类似,都是从唯一分解定理入手(整除的性质使然)。注意到题目给了很重要的一个条件就是\(\Sigma_{i = 1}^na_i <Y\),因此可以推出\(Z|Y\)。

证明:

前置条件1

\(a|b\)的充要条件是将a和b唯一分解以后a的每个质因子的次数小于等于b中这个质因子的次数。

前置条件2

\(x!\)含有的质因子p的个数为\(num = \lfloor \frac{x}{p} \rfloor + \lfloor \frac{x}{p^2} \rfloor + \lfloor \frac{x}{p^3} \rfloor + ....\)

证明见[计算n!中包含的质因子p的个数 - 脂环 - 博客园 (cnblogs.com)](https://i.cnblogs.com/posts/edit;postId=14757045)

因此将Z和Y唯一分解,对于某个质因子p,有:

Z中这个质因子的数量:\(num_Z(p) = \lfloor \frac{a_1}{p} \rfloor + \lfloor \frac{a_1}{p^2} \rfloor + \lfloor \frac{a_1}{p^3} \rfloor + .... + \lfloor \frac{a_1}{p^{c_1}} \rfloor + ... + \lfloor \frac{a_n}{p} \rfloor + \lfloor \frac{a_n}{p^2} \rfloor + \lfloor \frac{a_n}{p^3} \rfloor + .... + \lfloor \frac{a_n}{p^{c_n}} \rfloor\)

改写一下会得到:

$num_Z(p) = \lfloor \frac{a_1}{p} \rfloor + \lfloor \frac{a_2}{p} \rfloor + \lfloor \frac{a_3}{p} \rfloor + .... + \lfloor \frac{a_n}{p} \rfloor + ... $

Y中这个质因子的数量:

\(num_Y(p) = \lfloor \frac{Y}{p} \rfloor + \lfloor \frac{Y}{p^2} \rfloor + \lfloor \frac{Y}{p^3} \rfloor + .... + \lfloor \frac{Y}{p^{c}} \rfloor\)

因为\(\Sigma_{i = 1}^na_i <Y\),因此\(\lfloor \frac{a_1}{p} \rfloor + \lfloor \frac{a_2}{p} \rfloor + \lfloor \frac{a_3}{p} \rfloor + .... + \lfloor \frac{a_n}{p} \rfloor < \lfloor\frac{Y}{p}\rfloor\),其余项同理,又因为\(\Sigma_{i = 1}^na_i <Y\),因此最大的\(a_i < Y\),也即最大的\(c_i < c\),因此正确性得以保证。

所以只需要将Z和Y分解质因数,将每个质因数p的次数\(c_Z,c_Y\)作差然后除以\(c_X\)向下取整(因为要保证\(X | Y\)),算出来的所有数取min就得到答案了。

由于数的范围很大,因此要用到Pollard Rho算法进行大数分解。代码参考了2019-2020 ICPC Asia Xuzhou Regional Contest【徐州现场赛】_KobeDuu-CSDN博客大佬的博客,这个板子还是蛮好用的。求的时候只需要对X分解,因为最终要求x变成y的因子,Z肯定是Y!的因子因此Y、Z一起考虑,所以只关心X中有哪些质因子,找到X中的质因子然后算Y!和Z中这些质因子的次数即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+16;
LL n, 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){//只需要分解x 不需要分解y 因为最终要求x变成y的因子
        map<LL,LL> mp;  LL ans = 5e18;
        cnt = 0; getfactor(x);
        for(int i = 0;i < cnt; ++i) mp[fac[i]]++;
        map<LL,LL>::iterator it = mp.begin();
        for(it = mp.begin(); it != mp.end(); it++) {
        	LL nowp = it->first;//对应的质因子
        	LL cnt = 0;
        	for(int i = 1; i <= n; i++) {
        		cnt += cal(a[i], nowp);
        	}
        	ans = min(ans, (cal(y, nowp) - cnt)/ (it->second));
        }
        return ans;
    }
}Q;
int main() {
	int t;
	cin >> t;
	while(t--) {
		cin >> n >> x >> y;
		for(int i = 1; i <= n; i++) cin >> a[i];
		//y分解质因子的次幂
		cout << Q.solve(n, x, y) << endl;
	}
	return 0;
}
上一篇:【洛谷4193】数字(简单数学题)


下一篇:使用python selenium进行自动化functional test