题目链接:http://codeforces.com/problemset/problem/1114/C
算是一个比较经典的数论题目,有一定的数论基础的话是不难想出来这个题目的做法的
题意:
求n的阶乘在b进制下末尾有多少个零
题解:
要完成这个题目,需要用到数论里的唯一分解定理;
唯一分解定理说的是任何一个大于一的正整数,都可以被分解为若干素数的乘积,而且形式唯一。
对于这个题目,我们通过进制的知识可以知道,在b进制下后边有几个零,代表这个数字是b的几次方的倍数
比如:100010(2) = 34(10) = 2 × 17是二的一次方的倍数
1100(2) = 12(10) = 4 × 3是二的二次方的倍数
于是题目便转化为了求n的阶乘最大能够被b的多少次方整除
由唯一分解定理我们可以知道,我们对于n的阶乘做唯一分解,同时对b做唯一分解,得到
b = p1 a1 · p2 a2 · p3 a3…
n! = p1 b1 · p2 b2 · p3 b3…
那么n的阶乘最大能够被b的多少次方整除就转换为了求min{b1 / a1,b2 / a2…}
我们考虑到n的阶乘的特殊性,即它是1 ~ n连续数字的乘积,而在1 ~ n中p1、p2…他们的倍数是周期性变换的,
比如6!在二进制下,1~6中2的倍数出现了3次,4的倍数出现了1次,我们对6的阶乘进行唯一分解
6! = 720 = 2 4 × 3 2 × 5 1 于是后边的零为4个
从而我们得知,对于n的阶乘,它的唯一分解中p的次数等于1~n中p的各个次方(从一次方开始)出现的次数之和
即sum{n / p1,n / p2,n / p3,…}
这样我们就可以求出n的阶乘最大能够被b的多少次方整除
PS:关于唯一分解的方法,参照求欧拉函数的方法,复杂度为log2n;注意不要爆longlong
代码
#include <cstring>
#include <bits/stdc++.h>
#define ll long long
#define MAXN 200003
using namespace std;
ll res[MAXN],num[MAXN];
int main()
{
ll n,b;
int k = 0;
scanf("%I64d%I64d",&n,&b);
for(ll i = 2;i <= sqrt(b);i++)
{
if(b % i == 0)
{
ll t = 0;
while(b % i == 0)
{
b /= i;
t++;
}
res[++k] = i;
num[k] = t;
}
}
if(b != 1)
{
res[++k] = b;
num[k] = 1;
}
ll ans = -1;
for(int i = 1;i <= k;i++)
{
ll T = 0;
ll t = 1;
while(t <= n / res[i])
{
t *= res[i];
T++;
}
//printf("%d\n",t);
ll temp = 0,sum = 0,cnt = 0;
for(ll j = T;j >= 1LL;j--)
{
temp = n / t - cnt;
cnt = n / t;
sum += temp * j;
//printf("T%I64d %I64d %I64d\n",temp,j,sum);
t /= res[i];
}
if(ans != -1)
ans = min(ans,sum / num[i]);
else
ans = sum / num[i];
}
printf("%I64d\n",ans);
return 0;
}