1311: Rightmost non-zero Digit in N!
时间限制: 2 Sec 内存限制: 128 MB提交: 67 解决: 3
题目描述
The expression N!, read as "N factorial," denotes the product of the first N positive integers, where N is nonnegative. So, for example
N N!
0 1
1 1
2 2
3 6
4 24
5 120
10 3628800
For this problem, you are to write a program that can compute the rightmost non-zero digit of the factorial for N in base B.
输入要求
In the first line there is an integer T (T <= 100), indicates the number of test cases.
In each case, there is a single line with integers N and B
(0<=N<=10^7, 2<=B<=10^4).
输出要求
For each case, the output format is “Case #c: ans”.
c is the case number start from 1.
ans is the answer of this problem. The ans is in base 10.
See the sample for more details.
假如输入
应当输出
提示
Case #1:
13*12*11*10*9*8*7*6*5*4*3*2*1=6227020800 base 10, which in base 3 is
121001222020102200000, so the right-most non-zero digit is 2.
#include <cstdio>
using namespace std;
typedef long long LL;
int T, N, B;
bool bp[10001];
int p[10001][20], num[10001];
LL fb[20], fn[20], nnum[20], cop[20], coprnz[20], mini;
void getp()
{
for (int i=2; i<10001; i++)
{
if (!bp[i])for (int j=i; j<10001; j+=i)
{
p[j][num[j]++] = i;
if(i!=j)bp[j] = 1;
}
}
}
LL mod_mult ( LL a, LL b, LL n )
{
LL ret = 0;
a = a % n;b = b % n;
while ( b >= 1 )
{
if ( b & 1 )
{
ret += a;
if ( ret >= n ) ret -= n;
}
a = a << 1;
if ( a >= n ) a -= n;
b = b >> 1;
}
return ret;
}
LL mod_exp ( LL a, LL b, LL n )
{
LL ret = 1;
a = a % n;
if (b==1) return a;
while ( b >= 1 )
{
if ( b & 1 )
ret = mod_mult(ret,a,n);
a = mod_mult(a,a,n);
b = b >> 1;
}
return ret;
}
void exgcd(LL a, LL b, LL &d, LL &x, LL &y)
{
if (b==0)
{
d = a;x = 1;y = 0;
return;
}
exgcd(b, a%b, d, y, x); y-=x*(a/b);
}
LL inv(LL a, LL n)
{
LL x, d, y;
exgcd(a, n, d, x, y);
if (d!=1) return -1;
return (x+n)%n;
}
void gcoprnz(int cur)
{
int tB = cop[cur];
int base[tB+1];base[0] = 1;
for (int i=1; i<=tB; i++)
{
if (i%fb[cur])
{
base[i] = i * base[i-1] % tB;
}
else
{
base[i] = base[i-1];
}
}
LL ans = 1;
for (int t=N; t>0; t/=p[B][cur])
{
ans = ans * base[t%tB] % tB;
ans = ans * mod_exp(base[tB], t/tB, tB)%tB;
}
ans = ans * mod_exp(fb[cur], nnum[cur]%fn[cur], tB) % tB;
coprnz[cur] = ans;
//printf ("%I64d\n", ans);
}
LL china()
{
LL c[20];
for (int i=0; i<num[B]; i++)
{
c[i] = inv(B/cop[i], cop[i]) * B/cop[i] % B;
c[i] = c[i] * coprnz[i] % B;
}
LL res = 0;
for (int i=0; i<num[B]; i++)
{
res += c[i];
}
return res%B;
}
int main()
{
getp();
scanf ("%d", &T);
for (int ci=1; ci<=T; ci++)
{
scanf ("%d%d", &N, &B);
int b = B;
for (int i=0; i<num[B]; i++)
{
fb[i] = p[B][i];fn[i] = 0;cop[i] = 1;
while (b%p[B][i]==0)
{
b /= p[B][i];
fn[i]++;
cop[i] *= p[B][i];
}
//printf ("p[B][i] = %d, time = %d\n", fb[i], fn[i]);
}
mini = 0;
for (int i=0; i<num[B]; i++)
{
nnum[i] = 0;
for (int j=N/p[B][i]; j>0; j/=p[B][i])
{
nnum[i] += j;
}
if (nnum[i]/fn[i]<nnum[mini]/fn[mini]) mini = i;
}
for (int i=0; i<num[B]; i++)
{
gcoprnz(i);
}
int u = nnum[mini]/fn[mini];
for (int i=0; i<num[B]; i++)
{
//补数
int t = (nnum[i]/fn[i]-u)*fn[i];
coprnz[i] = coprnz[i] * mod_exp(fb[i], t, cop[i]) % cop[i];
//乘逆
int x = B/cop[i], v = inv(x, cop[i]);
coprnz[i] = coprnz[i] * mod_exp(v, u, cop[i]) % cop[i];
}
LL res = china();
printf ("Case #%d: ", ci);
printf ("%d\n", (int)res);
}
return 0;
}
/**************************************************************
Problem: 1311
User: 20124867
Language: C++
Result: 正确
Time:13 ms
Memory:1616 kb
****************************************************************/