
POJ 1150 The Last Non-zero Digit
超大组合数:求超大组合数P(n, m)的最后一个非零位。
4.1更加复杂的数学问题
模运算的世界
今天过节,管它什么节,对我来说都一样,来刷一题渲染一下节日气氛。
终于刷到【登峰造极——高级篇】了,我却一点登峰造极的感觉都没有
P(n, m)=n! / (n-m)!,问题归结于求n!的最后一个非零位。
先把n!中所有的10因子去掉,问题归结于求最后一位。但是10不是质因数,不好处理,退而求其次,将所有的2^a*5^b去掉,得到一个新数列f(1)…f(n),也即定义:
f(n) := n除以2^a*5^b,a和b分别是质因子2和5的个数
如果计算出了f(1)…f(n)中以3,7,9结尾的数的个数,就可以求出它们乘积的最后一位。因为3,7,9的n次方的最后一位是以4为周期的(其实2也是),这样就不用做很多乘法,也避免了溢出问题。至于怎么求,其实是一个递归的过程。并不先将2^a*5^b除掉,直接讨论n!,将其分为奇偶两个部分,比如 1 2 3 4 5 6-> 2 4 6| 1 3 5。而2 4 6可以进行除2^a这个操作,所以除2直到得到奇数数列,问题归结于奇数数列。
对奇数数列1 3 5 7 9 11 13 15 17 19,每10个单位跨度都有一个379,所以有n / 10 + (如果个位数大于379则加一)个,另外其中还可能有5的倍数,于是还需要除5递归。
得到f(1)*…*f(n)的最后一位后,还需要针对a和b进行讨论,如果a<b,也就是5的系数大,那么结果肯定是5(奇数*5的个位一定是5),否则乘以2a-b进行修正。这时候,又可以利用“2的n次方的最后一位是以4为周期”这个性质减少运算了。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
// 数n!的质因子x的个数
int fact_prime(int n, int x)
{
int res = 0;
while (n)
{
res += n / x;
n /= x;
}
return res;
}
// 定义 f(n) := n除以2^a*5^b,a和b分别是质因子2和5的个数
// f(1)...f(n)中以x(1 3 7 9)结尾的奇数的个数
int odd_end_with(int n, int x)
{
if (n == 0) return 0;
return n / 10 // 1.a 每10个数都有一个
+ (n % 10 >= x) // 1.b n的个位数可能产生一个
+ odd_end_with(n / 5, x); // 2. 迭代除以5,以满足5^b这个条件
}
// f(1)...f(n)中以x(1 3 7 9)结尾的数的个数
int end_with(int n, int x)
{
if (n == 0) return 0;
return end_with(n / 2, x) // 1. 数列中的偶数,迭代以满足除以2^a这个条件
+ odd_end_with(n, x); // 2. 数列中的奇数
}
int table[4][4] =
{
6, 2, 4, 8,//2^4 2 2^2 2^3 的最后一位
1, 3, 9, 7,//同理3
1, 7, 9, 3,//同理7
1, 9, 1, 9,//同理9
};
///////////////////////////SubMain//////////////////////////////////
int main(int argc, char *argv[])
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int n, m;
while (~scanf("%d%d", &n, &m))
{
m = n - m;
int two = fact_prime(n, 2) - fact_prime(m, 2);
int five = fact_prime(n, 5) - fact_prime(m, 5);
int three = end_with(n, 3) - end_with(m, 3);
int seven = end_with(n, 7) - end_with(m, 7);
int nine = end_with(n, 9) - end_with(m, 9);
if (two < five)
{
puts("5");
continue;
}
int res = 1;
if (two > five) res *= table[0][(two - five) % 4];
res *= table[1][three % 4];
res *= table[2][seven % 4];
res *= table[3][nine % 4];
res %= 10;
printf("%d\n", res);
}
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
system("out.txt");
#endif
return 0;
}
///////////////////////////End Sub//////////////////////////////////
| 14047162 | hankcs | 1150 | Accepted | 148K | 0MS | C++ | 1762B | 2015-04-04 14:58:05 |
Reference
http://bbezxcy.iteye.com/blog/2164453
知识共享署名-非商业性使用-相同方式共享:码农场 » POJ 1150 The Last Non-zero Digit 题解《挑战程序设计竞赛》
码农场
感觉不会有5结尾的情况哦