题目:
Trailing Zeroes
You task is to find minimal natural number N, so that N! contains exactly Q zeroes on the trail in decimal notation. As you know N! = 1*2*...*N. For example, 5! = 120, 120 contains one zero on the trail.
Input
Input starts with an integer T (≤ 10000), denoting the number of test cases.
Each case contains an integer Q (1 ≤ Q ≤ 108) in a line.
Output
For each case, print the case number and N. If no solution is found then print 'impossible'.
Sample Input
3
1
2
5
Sample Output
Case 1: 5
Case 2: 10
Case 3: impossible
题解:
解法一:
这是一个二分经典题,主要思路为定义一个求n!后面0的数量的函数,然后二分。为什么要二分呢,因为你用朴素算法方法求的话,每次都要循环1e8次,肯定会TL,用二分查找可以快很多。
(二分搜索,在有序序列查找值上非常方便。通过一次比较,可以把解的范围缩小一半。反复与区间的重点进行比较,就可以不断把解的范围缩小到原来的一半,最终在O(logn)次的比较之内求得最终的解。)
求n!后面0的数量请参考我的CSDN博客中的一篇文章:https://blog.csdn.net/qq_45328552/article/details/98206579
我会在代码里分析一下二分。
#include <iostream> using namespace std; typedef long long ll; int search(ll n) { int sum = 0; while(n) { sum += n/5; n /= 5; } return sum; } int main() { // freopen("input.txt","r",stdin); //注释!注释!!! int t, n; cin >> t; for(int i = 1; i <= t; i ++ ) { cin >> n; ll head = 0, tail = 5e8 + 5, mid; //定义二分的头和尾及mid,注意定义成long long类型,因为二分一般都是求非常大的数 while(head <= tail) //二分循环需要满足的条件,头小于尾 { mid = (head + tail) / 2; if(search(mid) < n) head = mid + 1; //search(mid)小于n,则所求值必在右边,mid以及mid左边的数都不满足,因此让头等于mid+1 else if(search(mid) > n) tail = mid - 1; //search(mid)大于n,所求值必在左边,mid及mid右边的数都不满足,因此让尾等于mid-1 else break; //等于则跳出循环,因为已经确定了要找的值 } if(head > tail) printf("Case %d: impossible ", i); //head > tail找到不满足条件还找不到,说明已经完全二分还找不到符合条件的数,输出false else printf("Case %d: %d ", i, mid - mid % 5); //根据上面的代码可以知道这是search(mid)=n符合条件的值,输出即可 } //输出mid-mid%5的原因是阶乘后面0的数量相同的数会有重复的而这里要求满足条件的最小值6、7、8和5的阶乘后面0的数量是相同的,6、7、8减去6、7、8 % 5后就变成满足条件的最小值了 return 0; }
解法二:
我同学用了打表的方法做出了这道题,也是很强了。打表的时候看着数据4、5一组很慢、很慢地通过,最后成功AC,真的很爽。
打表注意事项:1、明白阶乘为0的数量的最小数为5的倍数,从而以5为倍数进行循环。
2、提前打好表,使用标记数组的思维去打表。不要在循环体内一次一次地去打表,肯定TL;
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e8+10; int f[N]; int search(int n) { int num = 0; while(n) { num+=n/5; n/=5; } return num; } int main() { // freopen("input.txt", "r", stdin); //提交时注释,调试时取消注释!!! int t; cin >> t; for(int i=5;i<1e8+10;i+=5) { f[search(i)]=i; } int tmp; for(int i=0;i<t;i++) { scanf("%d",&tmp); if(f[tmp]!=0) printf("%d ",f[tmp]); else printf("impossible "); } return 0; }
皓齿蛾眉,命曰伐性之斧。 --《吕氏春秋 》