【Luogu】P3927 SAC E#1 - 一道中档题 Factorial
【题目】洛谷10月月赛R1 提高组
【题意】求n!在k进制下末尾0的个数,n<=1e18,k<=1e16。
【题解】考虑10进制末尾0要考虑2和5,推广到k进制则将k分解质因数。
每个质因数在n!中的数量,以2为例是n/2+n/4+n/8...这样统计。(含x个就被统计x次)
最后得到凑出的k的个数就可以得到末尾0的个数。
分解质因数复杂度O(√k),也使用pollard rho算法可以加速。
#include <iostream> #include <stdlib.h> #include <string.h> #include <algorithm> #include <stdio.h> const int Times = 10; const int N = 5500; using namespace std; typedef long long LL; LL ct, cnt; LL fac[N], num[N]; LL gcd(LL a, LL b) { return b? gcd(b, a % b) : a; } LL multi(LL a, LL b, LL m) { LL ans = 0; a %= m; while(b) { if(b & 1) { ans = (ans + a) % m; b--; } b >>= 1; a = (a + a) % m; } return ans; } LL quick_mod(LL a, LL b, LL m) { LL ans = 1; a %= m; while(b) { if(b & 1) { ans = multi(ans, a, m); b--; } b >>= 1; a = multi(a, a, m); } return ans; } bool Miller_Rabin(LL n) { if(n == 2) return true; if(n < 2 || !(n & 1)) return false; LL m = n - 1; int k = 0; while((m & 1) == 0) { k++; m >>= 1; } for(int i=0; i<Times; i++) { LL a = rand() % (n - 1) + 1; LL x = quick_mod(a, m, n); LL y = 0; for(int j=0; j<k; j++) { y = multi(x, x, n); if(y == 1 && x != 1 && x != n - 1) return false; x = y; } if(y != 1) return false; } return true; } LL pollard_rho(LL n, LL c) { LL i = 1, k = 2; LL x = rand() % (n - 1) + 1; LL y = x; while(true) { i++; x = (multi(x, x, n) + c) % n; LL d = gcd((y - x + n) % n, n); if(1 < d && d < n) return d; if(y == x) return n; if(i == k) { y = x; k <<= 1; } } } void find(LL n, int c) { if(n == 1) return; if(Miller_Rabin(n)) { fac[ct++] = n; return ; } LL p = n; LL k = c; while(p >= n) p = pollard_rho(p, c--); find(p, k); find(n / p, k); } int main() { LL n,kind; scanf("%lld%lld",&kind,&n); ct = 0; find(n, 120); sort(fac, fac + ct); num[0] = 1; int k = 1; for(int i=1; i<ct; i++) { if(fac[i] == fac[i-1]) ++num[k-1]; else { num[k] = 1; fac[k++] = fac[i]; } } cnt = k; LL ans=(1ll<<60); for(int i=0;i<cnt;i++){ LL as=0,N=kind/fac[i]; while(N){ as+=N; N/=fac[i]; } as/=num[i]; ans=min(as,ans); } if(ans==1ll<<60)ans=0; printf("%lld",ans); return 0; }