详细代码可以fork下Github上leetcode项目,不定期更新。
练习题如下:
总算刷到了登峰造极,看了一些关于模运算的相关知识,但还是云里雾里,索性就直接开题,该系列每题都开篇博文讲解吧,知识点比较多。
此题推荐博文:http://www.hankcs.com/program/algorithm/poj-1150-the-last-non-zero-digit.html
更详细的解法:http://duanple.blog.163.com/blog/static/7097176720081016113033592/
数论入门推荐书籍:《初等数论及其应用》.
思路:
进行质因数分解,分解2和5,因为只有2和5的乘积才能构成10。在一个序列中:
1 2 3 4 5 6 7 8 9 10
因数2的个数永远大于因数5的个数
所以说,我们只需要求出多出来的2的个数即可,这些将会影响非零位数的最终结果。
2的质因数个数如何求解:
1 2 3 4 5 6 7 8 9 10
可以分解为:
1 3 5 7 9 | 2 4 6 8 10
前半部分个数:0
后半部分个数:5 + f{1,2,3,4,5}
没错,除了个2之后,能够得到5个质因数
该问题就变成了子问题{1,2,3,4,5}的质因数个数
递归解决,复杂度O(logn)
代码如下:
public int total2(int n){
if (n == 0) return 0;
return n / 2 + total2(n / 2);
}
质因数5的个数如何?和2一个道理,所以有:
public int total5(int n){
if (n == 0) return 0;
return n / 5 + total5(n / 5);
}
所以综合下(递归版本):
public int fact_prime(int n, int x){
if (n == 0) return 0;
return n / x + fact_prime(n / x, x);
}
迭代版本:
public int fact_prime_iter(int n, int x){
int res = 0;
while (n > 0){
res += n / x;
n = n / x;
}
return res;
}
接着,把所有的2和5的质因数全部排除后,剩余了什么?
1 2 3 4 5 6 7 8 9 10
1 1 3 1 1 3 7 1 9 1
只剩下{1,3,7,9}了,那么为了能够计算非零位数,统计这些1,3,7,9的个数就完事了。
统计蛮取巧,分成奇数列和偶数列
1 3 5 7 9 | 2 4 6 8 10
前半部分的个数分别为:1:2 3:1 7:1 9:1
后半部分的个数分别为:子问题{1,2,3,4,5}
子问题可以不去考虑了,前半部分怎么统计呢?
观察发现它包含有5的奇数倍项,但这奇数倍均除以5得到1,3,5,7....
所以变成了前部部分的子问题了...
所以问题的关键就变成了求解1,3,7,9各自的个数了
其实都不需要求,直接看出来了
当n = 10,1:1, 3:1, 7:1, 9:1
当n = 13?
1:2 3:2 7:1 9:1
因为13中有11和13个位数分别为1和3
所以给定n
我们有 g(n, x) = n / 10 + (n % 10 >= x) + g(n / 5, x);
n/10: 每10个数都有一个
n%19: n的个位数可能产生一个
好了,代码就出来了:
public int odd_end_with(int n, int x){
if (n == 0) return 0;
int res = n / 10;
res += (n % 10 >= x ? 1 : 0);
return res + odd_end_with(n / 5, x);
}
public int end_with(int n, int x){
if (n == 0) return 0;
return end_with(n / 2, x) + odd_end_with(n, x);
}
最后如何计算 N!(N−M)! ?
很简单,因为N-M完全包含N,所以直接把统计结果减掉即可,最终代码如下:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.StringTokenizer;
public class Main{
String INPUT = "./data/judge/201708/P1150.txt";
public static void main(String[] args) throws IOException {
new Main().run();
}
int[][] table = {
{6, 2, 4, 8},
{1, 3, 9, 7}, // 3
{1, 7, 9, 3}, // 7
{1, 9, 1, 9}, // 9
};
void solve() {
while (more()){
int N = ni();
int M = ni();
// 分解质因数 2 和 5 并且分别统计 2 和 5 的个数
int two = fact_prime(N, 2) - fact_prime_iter(N - M, 2);
int five = fact_prime(N, 5) - fact_prime_iter(N - M, 5);
int remain = two - five;
// 统计 1 3 7 9 的个数
int three = end_with(N, 3) - end_with(N - M, 3);
int seven = end_with(N, 7) - end_with(N - M, 7);
int nine = end_with(N, 9) - end_with(N - M, 9);
int ans = 1;
ans *= remain == 0 ? 1 : table[0][remain % 4];
ans *= three == 0 ? 1 : table[1][three % 4];
ans *= seven == 0 ? 1 : table[2][seven % 4];
ans *= nine == 0 ? 1 : table[3][nine % 4];
ans %= 10;
out.println(ans);
}
}
public int fact_prime(int n, int x){
if (n == 0) return 0;
return n / x + fact_prime(n / x, x);
}
public int fact_prime_iter(int n, int x){
int res = 0;
while (n > 0){
res += n / x;
n = n / x;
}
return res;
}
public int odd_end_with(int n, int x){
if (n == 0) return 0;
int res = n / 10;
res += (n % 10 >= x ? 1 : 0);
return res + odd_end_with(n / 5, x);
}
public int end_with(int n, int x){
if (n == 0) return 0;
return end_with(n / 2, x) + odd_end_with(n, x);
}
FastScanner in;
PrintWriter out;
void run() throws IOException {
boolean oj;
try {
oj = ! System.getProperty("user.dir").equals("F:\\java_workspace\\leetcode");
} catch (Exception e) {
oj = System.getProperty("ONLINE_JUDGE") != null;
}
InputStream is = oj ? System.in : new FileInputStream(new File(INPUT));
in = new FastScanner(is);
out = new PrintWriter(System.out);
long s = System.currentTimeMillis();
solve();
out.flush();
if (!oj){
System.out.println("[" + (System.currentTimeMillis() - s) + "ms]");
}
}
public boolean more(){
return in.hasNext();
}
public int ni(){
return in.nextInt();
}
public long nl(){
return in.nextLong();
}
public double nd(){
return in.nextDouble();
}
public String ns(){
return in.nextString();
}
public char nc(){
return in.nextChar();
}
class FastScanner {
BufferedReader br;
StringTokenizer st;
boolean hasNext;
public FastScanner(InputStream is) throws IOException {
br = new BufferedReader(new InputStreamReader(is));
hasNext = true;
}
public String nextToken() {
while (st == null || !st.hasMoreTokens()) {
try {
st = new StringTokenizer(br.readLine());
} catch (Exception e) {
hasNext = false;
return "##";
}
}
return st.nextToken();
}
String next = null;
public boolean hasNext(){
next = nextToken();
return hasNext;
}
public int nextInt() {
if (next == null){
hasNext();
}
String more = next;
next = null;
return Integer.parseInt(more);
}
public long nextLong() {
if (next == null){
hasNext();
}
String more = next;
next = null;
return Long.parseLong(more);
}
public double nextDouble() {
if (next == null){
hasNext();
}
String more = next;
next = null;
return Double.parseDouble(more);
}
public String nextString(){
if (next == null){
hasNext();
}
String more = next;
next = null;
return more;
}
public char nextChar(){
if (next == null){
hasNext();
}
String more = next;
next = null;
return more.charAt(0);
}
}
}