題目傳送門:http://poj.org/problem?id=1150
http://poj.org/problem?id=3406
1150的題意是給你一個nPm, 求這個數從低位開始找第一個非零的數字。
#include <iostream>
#include <cstdio>
using namespace std;
int table[4] = {6, 2, 4, 8};
int step[10] = {0,0,1,-1,2,-1,0,1,-1,2};
const int mod = 4;
int getstep(int x)
{
int s = 0;
while (x) {
s = (s + x / 5) % mod;
if (x % 5 == 2) s = (s + 1) % mod;
if (x % 5 == 4) s = (s + 2) % mod;
x /= 5;
}
return s;
}
int main()
{
int n,m;
while (~scanf("%d%d",&n,&m)) {
if (n <= 1 || m == 0) {
printf("1\n");
} else if (m == 1) {
printf("%d\n",n);
} else {
printf("%d\n",table[(getstep(n) + 4 - getstep(n - m)) % mod]);
}
}
return 0;
}
暴力一定超時,從網上找了一篇blog,真的很牛。
http://www.tuicool.com/articles/naaUZ3
大致的意思是每對當前的中間過程的得數 乘一個數,會對最後一位施加一個影響,對於>1的數字的階乘,其最後一位一定是2 4 6 8中的一個,不妨令table[] = {6, 2, 4, 8}(2的4次方,1次方,2次方,3次方),而每乘一個數就是對當前最後非零位的一次改變,對應在table這個表中就體現爲左移,右移或者不動,這個規律也很好找,試一下就好了,就是代碼中的step。
由於排列數P(n,m) = n!/(n - m)! 所以計算出n!移動的步數,在計算出(n-m)!移動的步數,就可以得到結果了。
#include <cstdio>
#include <iostream>
using namespace std;
int n,m;
int table[4][4] = {
6, 2, 4, 8,
1, 3, 9, 7,
1, 7, 9, 3,
1, 9, 1, 9,
};
int getprime(int n, int x)
{
int ret = 0;
while (n) {
ret += n / x;
n /= x;
}
return ret;
}
int getodd(int n, int x)
{
if (n == 0) return 0;
return getodd(n / 5, x) + (n % 10 >= x) + (n / 10);
}
int getend(int n, int x)
{
if (n == 0) return 0;
return getend(n / 2, x) + getodd(n, x);
}
int main()
{
while (~scanf("%d%d",&n,&m)) {
m = n - m;
int two = getprime(n,2) - getprime(m,2);
int five = getprime(n,5) - getprime(m,5);
int three = getend(n,3) - getend(m,3);
int seven = getend(n,7) - getend(m,7);
int nine = getend(n,9) - getend(m,9);
int ans = 1;
if (five > two) {
puts("5");
continue;
}
ans = ans * table[1][three % 4] % 10;
ans = ans * table[2][seven % 4] % 10;
ans = ans * table[3][nine % 4] % 10;
if (two > five) {
ans = ans * table[0][(two - five) % 4] % 10;
}
printf("%d\n",ans);
}
}
這種方法是比較通用的方法,通過計算n!的因子數來確定最後一位。首先要先把所有因子爲10的全部去掉,這樣後面就不會有0了,但10不是質數,並不容易找出,不妨 找出全部因子爲2和因子爲5的個數,這樣就可以找到因子爲10的個數了。剔除2和5之後,剩下的數字一定是末尾爲3 7 9的數字的積,而這些數字自身的乘方末尾數字是有循環節的,最後不要忘了把多餘的因子2補上,而2的乘方末位也是循環的。
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m;
int table[4][4] = {
6,2,4,8,
1,3,9,7,
1,7,9,3,
1,9,1,9,
};
int getprime( int n, int x)
{
int ret = 0;
while (n) {
ret += n / x;
n /= x;
}
return ret;
}
int getodd (int n, int x)
{
if (n == 0) {
return 0;
} else {
return (n / 10) + (n % 10 >= x) + getodd(n / 5, x);
}
}
int getend (int n, int x)
{
if (n == 0) return 0;
return getend(n / 2, x) + getodd(n, x);
}
int main()
{
while (~scanf("%d%d",&n,&m)) {
int two = getprime(n, 2) - getprime(m, 2) - getprime(n - m, 2);
int five = getprime(n, 5) - getprime(m, 5) - getprime(n - m, 5);
if (five > two) {
puts("5");
continue;
}
int three = getend(n, 3) - getend(m, 3) - getend(n - m, 3);
int seven = getend(n, 7) - getend(m, 7) - getend(n - m, 7);
int nine = getend(n, 9) - getend(m, 9) - getend(n - m, 9);
three += 2 * nine; nine = 0;
int ans = 1;
ans *= table[1][three % 4];
ans *= table[2][seven % 4];
ans *= table[3][nine % 4];
if (two > five) {
ans *= table[0][(two - five) % 4];
}
printf("%d\n",ans % 10);
}
return 0;
}
而對於組合數,第一種方法就不適用了,因爲末位數字不一定就是table中的數字了,所以使用了第二種方法,不過要注意的是,組合數中分母寫成階乘形式,末位爲3的個數可能會比分子上的多,所以我們把9拆開,拆成兩個3.