THE FACTORIAL PROBLEM

07 Oct 2013

codechef上发现一个挺有意思的题目。大意是这样的:

Problem Statement:
There is a single positive integer T on the first line of input (equal to about 100000). 
It stands for the number of numbers to follow. 
Then there are T lines, each containing exactly one positive integer number N, 1 <= N <= 1000000000.

Output
For every number N, output a single line containing the single non-negative integer Z(N).
Z(N) function outputs the number of Trailing Zero's in Factorial of N.

Sample Input:
6
3
60
100
1024
23456
8735373

Sample Output:
0
14
24
253
5861
2183837

上面的Sample Input,第一行的6表示下面有六个数字,对于下面的这6个数字,我们假设他们某个数字阶乘的值是m,你需要计算的是m的最后面有几个0。

比如给你一个数字:5

我们很容易计算出它的阶乘:5! = 5×4×3×2×1=120

120后面有1个0, 所以结果为1。

这样我们很容易得到下面这段代码。

#! /usr/bin/python
# -*- coding: utf-8 -*-
import math

t = input()
while t > 0:
    t -= 1
    n = input()

    #计算阶乘
    factorial_of_n = math.factorial(n)

    #计算后面有几个0
    num_of_0 = 0
    tmp = str(factorial_of_n)[::-1]    #把阶乘结果变成字符转并反转
    for bit in tmp:
        if int(bit) == 0:
            num_of_0 += 1
        else:
            break

    print num_of_0

简单吧,可是它消耗时间的本领实在太强了,输入的上界可是10的9次方啊,解出它的阶乘是个繁重的工作,会消耗很多时间。所以,这不是个好办法。

既然计算阶乘消耗的时间和内存太多,有没有可能不计算阶乘就得出结果呢?

答案是肯定的。我们先来看看60这个数,它的阶乘是下面这60个数字相乘的结果:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

我还是先把它算出来吧: 8320987112741390144276341183223364380754172606361245952449277696409600000000000000 (容易得出结果是14)

我们知道几个数相乘,要让结果的后面有0,只有一种情况:这几个数的因子里有2和5。这样就方便了,我们只要计算着所有1~n这些数的因子中一共有多少的2,多少个5就行了,结果就是(2,5)这样的组合有多少对。也就是因子2的个数和因子5的个数的最小值。我们拿小一些的数字举例,比如5吧,1,2,3,4,5里,因子2的个数是3(2有一个,4有2个);因子5的个数是1(只有5有一个)。那么,我们的结果就是1。

等等,是不是还有更简单的做法?不能忽略这样一个事实:因子2的个数总是不小于因子5的个数的!我们只要计算因子5的个数就行了,那就是我们要的结果!

那么,如何更快地计算因子5的个数呢?

我们继续拿60举例。

打眼一看,只有下面这些数有因子5:

5 10 15 20 25 30 35 40 45 50 55 60

它们的个数是m = 60 / 5。除以5就可以得到这些数的个数了,是12个。现在我们先记下12这个数,因为已经记下了12个因子5,那么我们就可以把它们删掉,上面的数每个去掉一个5,得到:

1 2 3 4 5 6 7 8 9 10 11 12

那么剩下的这几个数里有多少的因子5呢?

5 10

只要 12 / 5 除一下就出来了。现在结果是12+2=14在把上面剩下的数每个去掉一个5,得到:

1 2

没有了,因为2 / 5 = 0。

我们得到了结果:14。

回顾一下上面的步骤:

60 / 5 = 12
12 / 5 = 2
2 / 5 = 0 
结果就是:12 + 2 + 0

好了,可以给出代码了。一共两个,一个是网上找的,匪夷所思的写法。

代码一:

#include "stdio.h"
#include "stdlib.h"

#define MAXRANGE 100000

long int Display(long int n, long int v);
long int Divide(long int n, long int v);

typedef long int (*fMethod) (long int n, long int v);
fMethod job[2] = { Display, Divide };

long int Display(long int n, long int v) {
    printf("%d\n", n);
    return 0;
}

long int Divide(long int n, long int v) {
    v = v/5;
    return job[v==0?0:1](n+v,v);
}

int main() {
    long int nRange;
    long int nArray[MAXRANGE];
    long int i,j,k;
    scanf("%ld", &nRange);

    for(i = 0; i < nRange; i++) {
        scanf("%ld", &k);
        nArray[i] = k;
    }

    for(j = 0; j < nRange; j++) {
        long int n = (nArray[j]<<1)/10;
        job[n==0?0:1](n, n);
    }

    return 0;
}

代码二:

#include "stdio.h"
#include "stdlib.h"
#define MAXRANGE 100000

int main() {
    long int nRange;
    long int nArray[MAXRANGE];
    long int i,j,k;
    scanf("%ld", &nRange);

    for(i = 0; i < nRange; i++) {
        scanf("%ld", &k);
        nArray[i] = k;
    }

    for(j = 0; j < nRange; j++) {
        long int m = nArray[j];
        long int n = 0;
        while(m = m/5) {
            n+=m;
        }
        printf("%d\n", n);
    }

    return 0;
}

VIM粘贴板小结

29 Jul 2013

一直用vim,落伍的人用落伍的文本编辑器啊。

vim picture

vim的粘贴板一直很让我蛋疼,早上又是翻文档又是上网查终于明白了,贴出来共享了。

  • 先说系统粘贴板

从vim拷贝到系统粘贴板:"+y

从系统粘贴板拷贝到vim:"+p 或者 Shift + insert


  • 好了,粘贴板

vim里面, 系统粘贴板跟+寄存器是关联的,所以你对寄存器+的任何复制和粘贴都直接影响到系统粘贴板。vim有12个粘贴板(在我的机器上试了一下,不止12个,任何字母都可以作为粘贴板的表示),分别是0、1、2、3、4、5、6、7、8、9、a、”、+;用:reg命令可以查看各个粘贴板里的内容。

要将vim的内容复制到某个粘贴板,需要进入normal模式,选择内容,按"Ny完成复制,其中N是粘贴板号,例如把内容复制到粘贴板a,选中内容后按"ay就可以了。有几点需要注意:

"号粘贴板(临时粘贴板)直接按y就复制了,直接按p就粘贴了。

+号粘贴板是系统粘贴板,用"+y将内容复制到粘贴板后可以使用Ctrl+V将其粘贴到其它文档(如gedit、firefox)中,同理,要在其它地方用Ctrl+C或者右键复制的内容复制到vim中,需要在normal模式下按"+p

要将vim某个粘贴板的内容粘贴进来,需要进入normal模式,按下"Np,其中·N是粘贴板号。

注意:vim.gtk或vim.gnome才能使用系统全局粘贴板,默认的vim.basic是看不到+号寄存器的。