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

题目背景

数据已修改

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;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值