免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 7710 | 回复: 17
打印 上一主题 下一主题

阶乘的最右一位非零数字求和 [复制链接]

论坛徽章:
1
荣誉会员
日期:2011-11-23 16:44:17
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-05-15 19:57 |只看该作者 |正序浏览
Problem
对于小于25000的自然数n,求阶乘n!,(n-1)!,(n-2)!...3!,2!,1!最右边的非零数之和。

例如:

当n=5时,
5!=120,右边非零数为2;
4!=24,右边非零数为4;
3!=6,右边非零数为6;
2!=2,右边非零数为2;
1!=1,右边非零数为1。
其右边的非零数之和为15。

Input
本题有多组数据,每组数据包含一个正整数N(N不大于25000)占一行。

Output
对给定的每组输入数据,输出一个整数。每个结果占一行。不要输出额外的空行。

Sample Input
5
10
1

Sample Output
15
39
1

部份结果如下:
11
47

111
543

1111
5547

11111
55567

111111
555455

1111111
5555481

11111111
55555817

111111111
555555081

1111111111
5555555731

11111111111
55555555603

111111111111
555555554817

1111111111111
5555555553031

11111111111111
55555555553495

论坛徽章:
1
荣誉会员
日期:2011-11-23 16:44:17
18 [报告]
发表于 2006-07-17 13:20 |只看该作者
上面这个程序是可行的
用f(n)表示n的最右非零数字,则f(t)=f(t*6) (t=2,4,6,8 )
所以当k是4的倍数时,有:
f(k*5)=f(k/2*10)=f(k/2)=f(k/2*6)=f(k*3)

但好象这也只能得到O(n)的算法

论坛徽章:
0
17 [报告]
发表于 2006-07-16 20:54 |只看该作者
#include <stdio.h>
long  calc( long n)
{
        long total_n = 0;//1!到n!的所有的最右边的非0数字之和
        int left_n = 1;
        for(long i = 1; i <= n; i++)
        {
                long temp = i;
                while(temp%5 == 0)
                {
                        temp /= 5;
                        temp *= 3;
                }
                left_n = ( left_n * temp )%10;
       
                total_n += left_n;
        }

    return total_n;
}

int main(int argc, char* argv[])
{
        long i = 111111111;
    printf("calc(%lld) = %lld  \n", i,  calc(i) );
        return 0;
}

论坛徽章:
0
16 [报告]
发表于 2006-07-16 19:27 |只看该作者
上次想的太简单了,今天花了我半天时间才写个程序,算法学的不好,也不知道复杂度是多少,贴来与大家分享,不对的还望指正:)
//sum[MAX_N + 1]用来存放n!的个位数,所以对的n的上限受数组大小的限制,可以考虑数组的大小只有100,然后方的时候n!的位
//数放在sum[n%100]中
#include <stdio.h>
#define MAX_N 111111111
long sum[MAX_N + 1];
long  calc( long n)
{
        sum[0] = 1;
        long temp;
        long total_n = 0;//1!到n!的所有的最右边的非0数字之和
        for(long i = 1; i <= n; i++)
        {
                if(i%5 != 0)//不能被5整除
                {
                        sum[i] = ( sum[i - 1] * i )%10;
                }
                else if(i%25 != 0)//能被5整除
                {
                        temp = i/5;
                       
                        if(temp%2 == 0)
                        {
                                sum[i] = ( temp/2 )%10;
                                                                sum[i] = ( sum[i] * sum[i - 1] )%10;
                        }
                        else
                        {
                                sum[i] = ( temp )%10;
                                sum[i] = ( sum[i] * ( ( (i - 1)/2 )%10) )%10;
                                sum[i] = ( sum[i] * sum[i - 2] )%10;
                        }
                }
                else if(i%125 != 0)//能被5的2次方整除
                {
                        temp = i/25;
                        if(temp%2 == 0)
                        {
                                sum[i] = 1;
                                sum[i] = ( sum[i] * ( (i - 1)%10 ))%10;
                                sum[i] = ( sum[i] * ( ( (i - 2)/2 )%10) )%10;
                            sum[i] = ( sum[i] * sum[i - 3] )%10;
                                sum[i] = (sum[i] * (temp/2) )%10;
                               
                        }
                        else
                        {
                                sum[i] = 1;
                                sum[i] = ( sum[i] * ( ( (i - 1)/2 )%10) )%10;
                                sum[i] = ( sum[i] * ( (i - 2)%10 ))%10;
                                sum[i] = ( sum[i] * ( ( (i - 3)/2 )%10) )%10;
                                sum[i] = ( sum[i] * sum[i - 4] )%10;
                                sum[i] = (sum[i] * temp )%10;
                               
                        }
                }
                else if(i%625 != 0)//能被5的3次方整除
                {
                        temp = i/125;
                       
                        if(temp%2 == 0)
                        {
                                sum[i] = 1;
                                sum[i] = ( sum[i] * ( (i - 1)%10 ))%10;
                                sum[i] = ( sum[i] * ( ( (i - 2)/2 )%10) )%10;
                                sum[i] = ( sum[i] * ( (i - 3)%10 ))%10;
                                sum[i] = ( sum[i] * ( ( (i - 4)/2 )%10) )%10;
                                sum[i] = ( sum[i] * sum[i - 5] )%10;
                                sum[i] = ( sum[i] * (temp/2) )%10;
                        }
                        else
                        {
                                sum[i] = 1;
                                sum[i] = ( sum[i] * ( ( (i - 1)/2 )%10) )%10;
                                sum[i] = ( sum[i] * ( (i - 2)%10 ))%10;
                                sum[i] = ( sum[i] * ( ( (i - 3)/2 )%10) )%10;
                                sum[i] = ( sum[i] * ( (i - 4)%10 ))%10;
                                sum[i] = ( sum[i] * ( ( (i - 5)/10 )%10) )%10;
                                sum[i] = ( sum[i] * ( (i - 6)%10 ))%10;
                                sum[i] = ( sum[i] * ( ( (i - 7)/2 )%10) )%10;
                                sum[i] = ( sum[i] * sum[i - 8] )%10;
                                sum[i] = ( sum[i] * temp )%10;
                        }
                }
                else if(i%3125 != 0)//能被5的4次方整除
                {
                        temp = i/625;
                       
                        if(temp%2 == 0)
                        {
                                sum[i] = 1;
                                if((i - 2)%4 == 0)
                                {
                                        sum[i] = ( sum[i] * ( ( (i - 2)/4 )%10) )%10;
                                        sum[i] = ( sum[i] * ( ( (i - 4)/2 )%10) )%10;
                                }
                                else
                                {
                                    sum[i] = ( sum[i] * ( ( (i - 2)/2 )%10) )%10;
                                        sum[i] = ( sum[i] * ( ( (i - 4)/4 )%10) )%10;
                                }
                                sum[i] = ( sum[i] * ( (i - 1)%10 ))%10;
                                sum[i] = ( sum[i] * ( (i - 3)%10 ))%10;
                                sum[i] = ( sum[i] * sum[i - 5] )%10;
                                sum[i] = ( sum[i] * (temp/2) )%10;
                        }
                        else
                        {
                                sum[i] = 1;
                                if((i - 1)%4 == 0)
                                {
                                        sum[i] = ( sum[i] * ( ( (i - 1)/4 )%10) )%10;
                                        sum[i] = ( sum[i] * ( ( (i - 3)/2 )%10) )%10;
                                }
                                else
                                {
                                    sum[i] = ( sum[i] * ( ( (i - 1)/2 )%10) )%10;
                                        sum[i] = ( sum[i] * ( ( (i - 3)/4 )%10) )%10;
                                }
                                sum[i] = ( sum[i] * ( (i - 2)%10 ))%10;
                                sum[i] = ( sum[i] * ( (i - 4)%10 ))%10;
                                sum[i] = ( sum[i] * ( ( (i - 5)/10 )%10) )%10;
                                sum[i] = ( sum[i] * ( (i - 6)%10 ))%10;
                                sum[i] = ( sum[i] * ( ( (i - 7)/2 )%10) )%10;
                                sum[i] = ( sum[i] * sum[i - 8] )%10;
                                sum[i] = ( sum[i] * temp )%10;
                        }
                }
                else if(i%15625 != 0)//能被5的5次方整除
                {
                        temp = i/3125;
                        if(temp%2 == 0)
                        {
                                sum[i] = 1;
                                if(temp%4 == 0)
                                {
                                           sum[i] = ( sum[i] * ( ( (i - 2)/2 )%10) )%10;
                                         sum[i] = ( sum[i] * ( ( (i - 4)/4 )%10) )%10;
                                         sum[i] = ( sum[i] * ( ( (i - 6)/2 )%10) )%10;
                                         sum[i] = ( sum[i] * (temp/4) )%10;
                                }
                                else
                                {
                                     sum[i] = ( sum[i] * ( ( (i - 2)/4 )%10) )%10;
                                         sum[i] = ( sum[i] * ( ( (i - 4)/2 )%10) )%10;
                                         sum[i] = ( sum[i] * ( ( (i - 6)/4 )%10) )%10;
                                         sum[i] = ( sum[i] * (temp/2) )%10;
                                }
                                sum[i] = ( sum[i] * ( (i - 1)%10 ))%10;
                                sum[i] = ( sum[i] * ( (i - 3)%10 ))%10;
                                sum[i] = ( sum[i] * ( ( (i - 5)/5 )%10) )%10;
                                sum[i] = ( sum[i] * sum[i - 7] )%10;
                               
                        }
                        else
                        {
                                sum[i] = 1;
                                if((i - 1)%4 == 2)
                                {
                                           sum[i] = ( sum[i] * ( ( (i - 1)/2 )%10) )%10;
                                         sum[i] = ( sum[i] * ( ( (i - 3)/4 )%10) )%10;
                                         sum[i] = ( sum[i] * ( ( (i - 5)/10 )%10) )%10;
                                         sum[i] = ( sum[i] * ( ( (i - 7)/4 )%10) )%10;
                                }
                                else
                                {
                                     sum[i] = ( sum[i] * ( ( (i - 1)/4 )%10) )%10;
                                         sum[i] = ( sum[i] * ( ( (i - 3)/2 )%10) )%10;
                                         sum[i] = ( sum[i] * ( ( (i - 5)/20 )%10) )%10;
                                         sum[i] = ( sum[i] * ( ( (i - 7)/2 )%10) )%10;
                                }
                                sum[i] = ( sum[i] * ( (i - 2)%10 ))%10;
                                sum[i] = ( sum[i] * ( (i - 4)%10 ))%10;
                                sum[i] = ( sum[i] * ( (i - 6)%10 ))%10;
                                sum[i] = ( sum[i] * sum[i - 8] )%10;
                                sum[i] = ( sum[i] * (temp) )%10;
                        }
                }
                else
                {
                        //对于25000以内的不可能走到这里,更确切的说是小于78125以内的
                        temp = i;
                       
                        int total2 = 0;
                        int total5 = 0;
                        int ii = 0;
                        while(temp%10 == 0)temp /= 10;
                       
                        while(temp%5 == 0){temp /= 5;total5++;}
                        sum[i] = 1;
                        if(total2 != total5)
                        {
                                while(total2 != total5)
                                {       
                                        while(temp%5 == 0){temp /= 5;total5++;}
                                        if(temp%2 == 0)
                                        {
                                                temp /= 2;
                                                total2++;
                                                if(total2 == total5)
                                                {
                                                        sum[i] = (sum[i] * temp)%10;
                                                        sum[i] = (sum[i] * sum[i - ii - 1])%10;
                                                }
                                        }
                                        else
                                        {
                                                sum[i] = (sum[i] * temp)%10;
                                                ii++;
                                                temp = i - ii;
                                        }
                                }
                        }
                        else
                        {
                                sum[i] = (sum[i - 1] * temp)%10;
                        }
                }

                total_n += sum[i];
        }

    return total_n;
}

int main(int argc, char* argv[])
{
        long i = 11111111;
                printf("calc(%lld) = %lld  \n", i,  calc(i) );
        return 0;
}


结果:
calc(11111111) = 55555817
Press any key to continue

论坛徽章:
0
15 [报告]
发表于 2006-07-14 23:00 |只看该作者
呵呵,是我想的太简单了

论坛徽章:
1
荣誉会员
日期:2011-11-23 16:44:17
14 [报告]
发表于 2006-07-14 08:58 |只看该作者
你的想法是错误的,个位数相乘并不能得到最后一位非零数字
如:24*25=600的最后一位非零数字是6,而不是4*5/10=2

论坛徽章:
0
13 [报告]
发表于 2006-07-14 08:26 |只看该作者

一个O(n)算法,不知道对不对

//
void print_n(int n)
{
        int left_n_1 = 1;//(n-1)!的最右边的非0数字,初始化为0!的最右边非0数字1
        int left_n;//n!的最右边的非0数字
        long total_n = 0;//1!到n!的所有的最右边的非0数字之和
        for(int i =1;i <= n; i++)
        {
                int ii = i;
                while(ii%10 == 0)ii /= 10;
                ii %= 10;//ii为i的最右边非0数字
                int temp = left_n_1*(ii);
                temp%10 == 0?left_n = temp/10:left_n = temp%10;
                total_n += left_n;
                printf("%d!  %d  total_n = %d\n", i, left_n, total_n);
                left_n_1 = left_n;
        }
}
int main(int argc, char* argv[])
{
        print_n(111);
        return 0;
}

打印111一下的结果:
第二列为n!的最右边飞0数字,第三列为和
1!  1  total_n = 1
2!  2  total_n = 3
3!  6  total_n = 9
4!  4  total_n = 13
5!  2  total_n = 15
6!  2  total_n = 17
7!  4  total_n = 21
8!  2  total_n = 23
9!  8  total_n = 31
10!  8  total_n = 39
11!  8  total_n = 47
12!  6  total_n = 53
13!  8  total_n = 61
14!  2  total_n = 63
15!  1  total_n = 64
16!  6  total_n = 70
17!  2  total_n = 72
18!  6  total_n = 78
19!  4  total_n = 82
20!  8  total_n = 90
21!  8  total_n = 98
22!  6  total_n = 104
23!  8  total_n = 112
24!  2  total_n = 114
25!  1  total_n = 115
26!  6  total_n = 121
27!  2  total_n = 123
28!  6  total_n = 129
29!  4  total_n = 133
30!  2  total_n = 135
31!  2  total_n = 137
32!  4  total_n = 141
33!  2  total_n = 143
34!  8  total_n = 151
35!  4  total_n = 155
36!  4  total_n = 159
37!  8  total_n = 167
38!  4  total_n = 171
39!  6  total_n = 177
40!  4  total_n = 181
41!  4  total_n = 185
42!  8  total_n = 193
43!  4  total_n = 197
44!  6  total_n = 203
45!  3  total_n = 206
46!  8  total_n = 214
47!  6  total_n = 220
48!  8  total_n = 228
49!  2  total_n = 230
50!  1  total_n = 231
51!  1  total_n = 232
52!  2  total_n = 234
53!  6  total_n = 240
54!  4  total_n = 244
55!  2  total_n = 246
56!  2  total_n = 248
57!  4  total_n = 252
58!  2  total_n = 254
59!  8  total_n = 262
60!  8  total_n = 270
61!  8  total_n = 278
62!  6  total_n = 284
63!  8  total_n = 292
64!  2  total_n = 294
65!  1  total_n = 295
66!  6  total_n = 301
67!  2  total_n = 303
68!  6  total_n = 309
69!  4  total_n = 313
70!  8  total_n = 321
71!  8  total_n = 329
72!  6  total_n = 335
73!  8  total_n = 343
74!  2  total_n = 345
75!  1  total_n = 346
76!  6  total_n = 352
77!  2  total_n = 354
78!  6  total_n = 360
79!  4  total_n = 364
80!  2  total_n = 366
81!  2  total_n = 368
82!  4  total_n = 372
83!  2  total_n = 374
84!  8  total_n = 382
85!  4  total_n = 386
86!  4  total_n = 390
87!  8  total_n = 398
88!  4  total_n = 402
89!  6  total_n = 408
90!  4  total_n = 412
91!  4  total_n = 416
92!  8  total_n = 424
93!  4  total_n = 428
94!  6  total_n = 434
95!  3  total_n = 437
96!  8  total_n = 445
97!  6  total_n = 451
98!  8  total_n = 459
99!  2  total_n = 461
100!  2  total_n = 463
101!  2  total_n = 465
102!  4  total_n = 469
103!  2  total_n = 471
104!  8  total_n = 479
105!  4  total_n = 483
106!  4  total_n = 487
107!  8  total_n = 495
108!  4  total_n = 499
109!  6  total_n = 505
110!  6  total_n = 511
111!  6  total_n = 517
Press any key to continue

当n = 111时结果为517,而LZ的为543,看一下大家的结果,望指正

论坛徽章:
1
荣誉会员
日期:2011-11-23 16:44:17
12 [报告]
发表于 2006-05-17 11:47 |只看该作者
原帖由 win_hate 于 2006-5-17 11:35 发表
呵呵,先保存一下嘛。

看看这个:

g(n) 表示 n! 分解中不含因子 5 的部分,若 n =(A_k....A_0)_5 则

g(n) = A_0! * g (A_k ... A_1)_5

这样就可以用动态规划把 g(n) 求出来。 _5 表示 5 进制

g(n)是有周期的吧
g(5k)=4^k 按4、6、4、6循环出现

论坛徽章:
1
荣誉会员
日期:2011-11-23 16:44:17
11 [报告]
发表于 2006-05-17 11:41 |只看该作者
公式的推导基本是正确的,下面看怎么用这个公式
为方便起见,下面的函数之间的*乘法是指模10的运算,即m*n=(m%10) * (n%10) % 10
可以看到,f(n)有下面的属性:若在m和n的5进制展开Ak, Bk中没有相同的阶,即不存在i, 使Ai>0, Bi>0同
时成立,则f(m+n)=f(m) * f(n),特殊情况下,f(Ak*5^k+...+Ai*5^i +...+A0)=f(Ak*5^k + ... + A_(i+1)*5^(i+1)) * f(Ai*5^i + .. + A0)      (F)
另外,由于在n!的因子中,2的个数一定比5的个数多,所以,若n>1,则f(n)的值只能是2、4、6、8,又6*n=n (n=2,4,6,,所以6具有1的性质,为了统一起见,我们定义f(0) = f(1) = 6,而不是1,这样最终的结果会多出6*2-1=11
设s(m,n)=f(m) + f(m+1) + ... + f(m+n-1),若m的5进制展开中最低阶比n的最高阶要大,则由(F),得到
s(m,n) = f(m)*f(0) + f(m)*f(1) + ... + f(m)*f(n-1),可以看到每个因子都差了一个常数f(m)
如果我们知道f(0), ... f(n-1)中2、4、6、8的个数,则计算f(m)后,可以得到s(m,n)的2、4、6、8的个数
如:s(0,5)=6+6+2+6+4,其中2有1个,4有1个,6有3个,8有0个,记录到数组cnt中,初始值为cnt[0]={1,1,3,0},如果要求s(5^2),则
s(5^2) 分成0-4, 5-9, 10-14, 15-19, 20-24 五个部份
由第一部份,得到1 1 3 0
由第二部份,f[5]=2,2分别与2、4、6、8相乘,得到4 8 2 6, 于是得到4有1个,8有个1,2有3个,6有0个,即3 1 0 1
同样,由第三部份,得到1 0 1 3
第四部分,得到1 0 1 3
第五部份,得到0 3 1 1
相加的和,得到6 5 6 8
用这个方法可以得到s(0,5^k)的结果,记入到数据cnt中
于是:
设n的5进制展开式为Ak*5^k + ... + A1 * 5 + A0,则
s(0,n)=s(0,Ak*5^k) + s(Ak*5^k, A_(k-1) * 5^(k-1)) + ... +s(Ak*5^k+...+A1*5, A0)
用相同的方法可以得到2、4、6、8的个数,记入数组part
于是sum(n)=2*part[0] + 4*part[1] + 6*part[2] + 8*part[3] - 11

论坛徽章:
0
10 [报告]
发表于 2006-05-17 11:35 |只看该作者
呵呵,先保存一下嘛。

看看这个:

g(n) 表示 n! 分解中不含因子 5 的部分,若 n =(A_k....A_0)_5 则

g(n) = A_0! * g (A_k ... A_1)_5

这样就可以用动态规划把 g(n) 求出来。 _5 表示 5 进制
  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP