您的位置:首页 > 其它

P3927 SAC E#1 - 一道中档题 Factorial

2017-10-10 19:52 357 查看

题目背景

数据已修改

SOL君(炉石主播)和SOL菌(完美信息教室讲师)是好朋友。

题目描述

SOL君很喜欢阶乘。而SOL菌很喜欢研究进制。

这一天,SOL君跟SOL菌炫技,随口算出了n的阶乘。

SOL菌表示不服,立刻就要算这个数在k进制表示下末尾0的个数。

但是SOL菌太菜了于是请你帮忙。

输入输出格式

输入格式:

每组输入仅包含一行:两个整数n,k。

输出格式:

输出一个整数:n!在k进制下后缀0的个数。

输入输出样例

输入样例#1:
10 40


输出样例#1:
2






说明

对于20%的数据,n <= 1000000, k = 10

对于另外20%的数据,n <= 20, k <= 36

对于100%的数据,n <= 10^12,k <= 10^12

update

1.一组数据

2.K不会==1

3.现在std没有爆long long

4.对数据有问题联系icy (建议大家不要面向数据编程)

这道题如果用传统的方法去做,似乎只能做20%的分。

不过我们换个角度想,

先取比较熟悉的例子,N!在十进制下末尾零的个数,即求N!中包含的2和5的个数的最小值。(显然每一个末尾零都需要2和5相乘来得到)

那么N!在K进制下末尾零的个数,也等价于是N!中包含的K的质因数的最小个数。(K进制含义就是逢K进1,此时便产生一个末位0)

这样一来,我们就可以先用筛法求出质数,然后再求K的质因数即每个质因数的个数。

求k的质因数只需求1~根号k的质因数,因为若k为合数,其至少有一个质因数在1~根号k之间,而最多只有一个大于根号k的质因数。

所以当最后 t!= 1时, t 必为 唯一一个大于 根号k的质因数(k必然不存在两个大于根号k的质因数),需要考虑。

然后求N!中的每个质数i的个数即为


这个不难看出,因为N!=N*(N-1)*(N-2)……*2*1

其中k就含有一个k的因数,k*k含有两个,以此类推。

最好ans初始值应为一个极大的数,如(1ll  << 60)

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 0;
inline int get(){
char c;
while((c = getchar()) < '0' || c > '9');
int cnt = c - '0';
while((c = getchar()) >= '0' && c <= '9') cnt = cnt * 10 + c - '0';
return cnt;
}
typedef long long LL;
const LL maxn = 1E6;
LL N,K,ans,totc=0;
LL cnt[maxn+10],a[maxn+10];
bool b[maxn+10];
int pr[maxn+10],totp=0;
int main(){
#ifdef lwy
freopen("in.txt","r",stdin);
/*	#else
freopen(".in","r",stdin);
freopen(".out","w",stdout);*/
#endif

memset(cnt,0,sizeof(cnt));
memset(a,0,sizeof(a));
memset(b,true,sizeof(b));
scanf("%lld %lld",&N,&K);
LL t = K;
totc = sqrt(K);
for(int i = 2; i <= totc; i++){
if(b[i]) pr[++totp] = i;
for(int i = 1; i <= totp; i++){
if(i * pr[i] > totc) break;
b[i * pr[i]] = false;
if(i % pr[i] == 0) break;
}
}
for(int i = 1; i <= totp; i++){
while(t % pr[i] == 0){//if(t % pr[i] == 0)
t /= pr[i];
cnt[i]++;
}
}
if(t != 1){
pr[++totp] = t;
cnt[totp] = 1;
}
for(int i = 1; i <= totp; i++){
LL k = pr[i];
while(k <= N){
a[i] += N / k;
k *= pr[i]; //k *= k;
}
}
ans = (1LL << 60);
for(int i = 1; i <= totp; i++){
LL k;
if(cnt[i] == 0) continue;
k = a[i] / cnt[i];
ans = min(ans,k);
}
printf("%lld",ans);
return 0;
}

原理虽然简单,但编写过程中出了两处//后的错误。

网上发现有直接计算每个数而不分质因数考虑的解法,大体原理是相同,但是去除了找质数的操作,所以更快些。

需要注意的是最后 t != 1 的情况,此时 t 为 大于 根号k的质数,也是k的一个质因数,需要考虑。

找错的时候用了对拍,

数据生成器

注意要使用<ctime>库,以及需要用srand(0)初始化随机函数

rand()∈[0,2^16) 可以根据需要调整。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<ctime>
#include<algorithm>
using namespace std;
const int MAXN = 0;
inline int get(){
char c;
while((c = getchar()) < '0' || c > '9');
int cnt = c - '0';
while((c = getchar()) >= '0' && c <= '9') cnt = cnt * 10 + c - '0';
return cnt;
}
int main(){
/* #ifdef lwy
#else
freopen(".in","r",stdin);
freopen(".out","w",stdout);
#endif*/
srand(time(0));
long long N,K;
N = rand() >> 10;
while(K == 1 || K == 0) K = rand() >> 10;
printf("%lld %lld",N,K);
return 0;
}


对拍代码如下,相应文件应提前建立,并且每个程序都应先编译好。

#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<windows.h>
using namespace std;
const int MAXN = 0;
inline int get(){
char c;
while((c = getchar()) < '0' || c > '9');
int cnt = c - '0';
while((c = getchar()) >= '0' && c <= '9') cnt = cnt * 10 + c - '0';
return cnt;
}
int main(){
/* #ifdef lwy
#else
freopen(".in","r",stdin);
freopen(".out","w",stdout);
#endif*/
int t = 100;
while(t--){
system("data > in.txt");
system("1 < in.txt > 1.out");
system("2 < in.txt > 2.out");
if(system("fc 1.out 2.out")) break;
}
system("pause");
return 0;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: