【代码超详解】LightOJ 1138 Trailing Zeroes (III)(思维,5 ms)

一、题目描述

在这里插入图片描述

二、算法分析说明与代码编写指导

要求 N! 的末尾有多少个 0,关键是看 N! 的表达式中含有多少个 5。因为 10 的 Q 次方的倍数的末尾一定是 Q 个 0,而 N! 的表达式中,容易发现 2 的次数肯定比 5 的次数多,所以我们只用数 5 的次数,就知道有多少个 10 参与了相乘。
每个 5 的倍数会为 N! 贡献 1 个 5。但每个 25 的倍数会再贡献 1 个 5,每个 125 的倍数会再贡献 1 个 5,……。总共贡献了 Q 个 5。列出方程:
在这里插入图片描述
通分,由等比数列的求和公式:
在这里插入图片描述
如果只记 5 的倍数的贡献,那么取 x = 1。再加上 25 的倍数的额外贡献,则取 x = 2,……。
x 到底要取多少呢?我们不妨先求出有 5 的更高次方的数开始额外贡献质因子 5 时,Q 至少要达到多少。
在这里插入图片描述设数列在这里插入图片描述
我们根据输入的 Q 的大小不同来确定接下来的计算步骤。用 lower_bound 函数可以查找 q 中第一个 ≥ q 的数的位置,记为 p。如恰有数列 q 中的某一项 *p = Q,那么答案正好就是 5 的 p - q 次方。
如果没有,则 * p > Q。先把 p 往前推一个位置,并设 x = p - q + 1,则有在这里插入图片描述
根据上面的表格可以知道,查找到 Q 在 q 中的位置后,可以算出 N 最大不能达到 5 的 x 次方,记为 dlim。只有 5 的 1,2,……,x - 1 次方的倍数贡献质因子 5。
设 t = Q / q[x - 1],t × q[x - 1] 这部分一共有 5 的 1,2,……,x - 1 次方这 x - 1 个数提供质因子 5。将相应的结果累加到 N 中去,然后将这部分从 Q 中扣除。剩余部分的 dlim 一定会变小(否则意味着 5 的高次方的贡献没有全部算上,但这与 t = Q / q[x - 1],t × q[x - 1] 两式的实际含义不符),要重新设定 dlim。如果这个过程中将剩余的 Q 换算成的 N 增加的幅度 d ≥ dlim,则意味着无解。因为 dlim 总是 5 的整数次方倍,这个 5 的整数次方倍本身会比 dlim 以前的任何一个贡献了质因子 5 的数再多贡献一个质因子 5,而这个 5 没有被算进去。
设 e[i] 代表 5 的 i 次方。
例如:
设 Q = 100,q[3] = 31,q[4] = 126。100 / 31 = 3,查表 e[3] = 125,所以 N 至少达到 125 × 3 = 375,Q = 100 - 31 × 3 = 7。
Q = 7 在 q[2] = 6 和 q[3] = 31 之间,7 / 6 = 1,查表 e[2] = 25,所以 N 至少达到 375 + 25 × 1 = 400,Q = 7 - 6 × 1 = 1。
Q = 1 在 q[1] = 1 和 q[2] = 6 之间,1 / 1 = 1,查表 e[1] = 5,所以 N 至少达到 400 + 5 × 1 = 405。计算完毕。
再例如:设 Q = 11,q[2] = 6,q[3] = 31。11 / 6 = 1,查表 e[2] = 25,所以 N 至少达到 25 × 1 = 25。Q = 11 - 6 × 1 = 5。
Q = 5 在 q[1] = 1 和 q[2] = 6 之间,5 / 1 = 5,查表 e[1] = 5,所以 N 至少达到 25 + 5 × 5 = 50。本轮循环增加的幅度 d = 5 × 5 = 25 = dlim,无解。因为这部分在 N! 的表达式中会给出 6 个质因子 5,而不是 5 个。换句话说,对应的实际状况是,想要 N 达到 50,必须允许 Q = 12,因为给出 6 个质因子 5,才能令 N = 25,因为 25 给的 2 个质因子 5 不能“拆开”。50 是 25 的倍数,情况类似。
(自行体会一下,N = 5,10,15,20,25,30,35,40,45,50,分别对应 Q = 1,2,3,4,6,7,8,9,10,12)

三、AC 代码(5 ms)

#include<cstdio>
#include<algorithm>
#pragma warning(disable:4996)
using namespace std;
unsigned e[16] = { 1 };
unsigned T, Q, q[16], t, * p, N, dlim, d; bool ok;
int main() {
	for (unsigned i = 1; i <= 13; ++i)e[i] = e[i - 1] * 5;
	for (unsigned i = 1; i <= 13; ++i)q[i] = (e[i] - 1) / 4;
	scanf("%u", &T);
	for (unsigned i = 1; i <= T; ++i) {
		scanf("%u", &Q); p = lower_bound(q + 1, q + 14, Q);
		if (*p == Q) { printf("Case %u: %u\n", i, e[p - q]); continue; }
		N = 0; --p; ok = true;
		while (p > q) {
			dlim = e[p - q + 1];
			t = Q / *p; Q -= t * *p; d = e[p - q] * t; if (d >= dlim) { ok = false; break; }
			N += d; --p;
		}
		if (ok == false) { printf("Case %u: impossible\n", i); continue; }
		printf("Case %u: %u\n", i, N);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值