2019 ICPC xuzhou E - Multiply(大数因数分解Pollard_rho 算法)
题目链接
题意:
\(Z=a_1!a_2!a_3!...a_n!\)
\(b_i=Z*X^i\)
求最大的\(i\)使得\(b_i\)是\(Y!\)的因子。
思路:
\(a_1+a_2+a_+...+a_n<Y\),不难证明:\(Z\)一定为 \(Y!\)的因子。
所以直接对\(X\)进行素因子分解,对每个因子\(p_i\)求得其在\(Z\)和\(Y!\)中的幂次数\(sumZ_i,sumY_i\),则\(sumY_i-sumZ_i\)为\(p_i\)在\(X^i\)中出现的最大幂次数。
故\(ans=min(\dfrac{sumY_i-sumZ_i}{sump_i})\)(\(sump_i\)为\(p_i\)在\(X\)中的幂次数)
code:
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
#include <set>
#define fi first
#define se second
#define pb push_back
// #define endl "\n"
#define debug(x) cout << #x << ":" << x << endl;
#define bug cout << "********" << endl;
#define all(x) x.begin(), x.end()
#define lowbit(x) x & -x
#define fin(x) freopen(x, "r", stdin)
#define fout(x) freopen(x, "w", stdout)
#define ull unsigned long long
#define ll long long
const double eps = 1e-5;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double pi = acos(-1.0);
const int mod = 1e9 + 7;
const int maxn = 2e5 + 10;
using namespace std;
int n;
ll s[maxn], x, y, z;
ll tot, p[maxn];
ll gcd(ll a, ll b){return b == 0 ? a : gcd(b, a % b);}
ll qmul(ll a, ll b, ll p){//快速乘
ll z = (long double)a / p * b;
ll ret = (ull)a * b - (ull)z * p;
return (ret + p) % p;
}
ll qpow(ll a, ll b, ll p){//快速幂
ll ret = 1;
while(b){
if(b & 1)ret = qmul(ret , a , p);
b >>= 1;
a = qmul(a, a, p);
}
return ret;
}
bool Miller_rabin(ll n){// Miller _ Rabin判断素数
if(n == 2)return true;
if(n < 2 || !(n & 1))return false;//特判
ll m = n - 1, k = 0;
while(!(m & 1))m >>= 1, k ++;// 求得2的幂次数
for(int i = 1; i <= 6; i ++){// 测试次数
ll a = rand() % (n - 1) + 1;// 随机生成a
ll x = qpow(a, m, n), y;
for(int j = 1; j <= k; j ++){
y = qmul(x, x, n);
if(y == 1 && x != 1 && x != n - 1)return false;
// a ^ 2 = 1(mod p), p为质数,若a不为 1或者 n - 1, 则不符合二次探测定理
x = y;
}
if(y != 1)return false;// 费马小定理的逆命题判断
}
return true;
}
ll Pollard_Rho(ll n){// 二次优化,减少了gcd的计算次数
ll z, x, y, g, c;
while(1){// 一定会找到一个因子
int i = 0, j = 1;
c = rand() % (n - 1) + 1;
y = x = rand() % (n - 1) + 1;// 随机初始化
z = 1;// 存 abs(x - y)的乘积
while(++ i){
x = (qmul(x, x, n) + c) % n;// x ^ 2 + c
z = qmul(z, abs(x - y), n);
if(!z || x == y)break; // z = 0时重新测试, x == y时即为跑完了环,也重新测试
if(i == j || i % 127 == 0){// 当i == j 或者 i % 127 = 0 时,判断gcd
g = gcd(n, z);
if(g > 1)return g;// 找到了一个因子
if(i == j)j <<= 1, y = x; // 倍增维护答案准确性
}
}
}
}
void pr(ll n){
if(n == 1)return ;
if(Miller_rabin(n)){
p[++ tot] = n;
return ;
}
ll a = Pollard_Rho(n);
while(n % a == 0)n /= a;
pr(a), pr(n); // 继续分解
}
ll cnt(ll x, ll p){
if(!x)return 0;
return x/p + cnt(x/p, p);
}
int main(){
int t;
scanf("%d", &t);
while(t --){
scanf("%d%lld%lld", &n, &x, &y);
for(int i = 1; i <= n; i ++)scanf("%lld", &s[i]);
tot = 0, pr(x);
ll ans = INF;
for(int i = 1; i <= tot; i ++){
ll ret = x, sum = 0;
while(ret % p[i] == 0)ret /= p[i], sum ++;
ll a = cnt(y, p[i]), b = 0;
for(int j = 1; j <= n; j ++){
b += cnt(s[j], p[i]);
}
ans = min(ans, (a - b)/sum);
}
printf("%lld\n", ans);
}
return 0;
}

浙公网安备 33010602011771号