题目大意
给出一个大小为 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=Z∗Xi,求出最大的 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,p3⋅⋅⋅pn。对于某质因子 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 (c−a)/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 pk∗q,显然 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 ⌊p⌊pn⌋⌋,也相当于是 ⌊ 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;
}