题目传送门
题意:
给你n<=1e18,k<=1e12,求n!(n的阶乘)转化为k进制后,有多少个后导0.
思路:
问题转化为求n%(k^r) == 0的r的最大值。
由于自己叙述太麻烦,就将官方题解贴出来。
The problem can be reduced to the following: finding the maximum r that n ! is divisible by br.
By prime factorization, we will have the following: b = p1y1·p2y2·…·pmym.
In a similar manner, we will also have: n ! = p1x1·p2x2·…·pmxm·Q (with Q being coprime to any pi presented above).
The process of finding p1, p2, …, pn, y1, y2, …, ym can be done by normal prime factorization of the value b.
The process of finding x1, x2, …, xm is a little bit more tricky since the integer they were originated (n !) is too huge to be factorized manually. Still the factorial properties gave us another approach: for each pi, we can do the following:
Initially, denote xi = 0.
Repeatedly do the following: add to xi, then divide n by pi. The loop ends when n is zero.
After all, we can obtain the final value r as of following: .
Total complexity: (as the number of prime factors of an integer b will not surpass ).
这个题解还好看懂,的那就是有一个地方有点难理解。如何处理n!,将n!表示成pi^xi中的xi求出来,其中我们只关注k分解出来的质因数,然后在此基础上处理n!,求出xi.边分解k边处理n!节约空间。
那么如何理解下面的代码可以求出xi是关键。
下面举个例子:
n = 125,i = 5;
首先将1-125之间可以整除5的数写出来,并标记每个数包含了几个5.
5(1) 10(1) 15(1) 20(1) 25(2) … 50(2) 75(2) … 95(1)…100(2) 125(3)
首先将c/i = 25;表示1-125之间有25个数包含了1个5,然后将c/=i;c = 25;然后c /5 = 5;表示有5个数包含了2个5,之后就是1个数包含了3个5(125)。
这个过程就像一层一层地求。如下:
5
5 5 5…5 5
5 5 5 5 5… 5 5 5… 5
那么第一列表示125。
求他的例子比如124也是一样的。
这个理解,问题就迎刃而解了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
typedef long long LL;
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define pii pair<int,int>
#define all(x) x.begin(),x.end()
#define mem(a,b) memset(a,b,sizeof(a))
#define per(i,a,b) for(int i = a;i <= b;++i)
#define rep(i,a,b) for(int i = a;i >= b;--i)
const int maxn = 3e5;
LL n = 0,b = 0;
int main(){
//std::ios::sync_with_stdio(false);
while(~scanf("%I64d %I64d",&n,&b)){
LL ans = 1000000000000000000LL;
//per(i,2,b){//变分解质因数,边处理,节约空间
for(LL i = 2;i <= b;++i){//变分解质因数,边处理,节约空间
if(1ll*i*i > b){//说明b是质数
i = b;
}
int cnt = 0;
while(b % i == 0){
b /= i;
++cnt;
}
if(cnt == 0){
continue;
}
LL tmp = 0,mul = 1;
/*
while(mul <= n/i){//不是mul <= n是为了防止mul超过longlong
mul *= i;
tmp += (n/mul);
}
*/
LL c = n;
while(c > 0){//还不如这样直接
tmp += (c/i);
c /= i;
}
ans = min(ans,tmp / cnt);
}
printf("%I64d\n",ans);
}
return 0;
}