题意:
很简单,给出正整数 n ,求出 n! 在十进制表示下的从最末非零位开始的总共 k 位。
n
≤
1
0
18
,
k
≤
3
n\leq 10^{18},k\leq 3
n≤1018,k≤3
分析:
有一个暴力的想法:直接从1枚举到N,然后把每个数的2的因数和5的因数除去,剩余部分乘起来
m
o
d
1
0
k
mod\ 10^k
mod 10k
然后我们剩下的,没有乘进去的2的整次幂肯定不比5的整次幂少。所以只要把多去掉的2的整次幂乘回来即可。
现在考虑如何使这个过程更加高效:
显然,我们可以很容易求出
n
!
(
m
o
d
5
)
n!(mod\ 5)
n!(mod 5),并且可以求出
n
!
n!
n!中5的整次幂有多少。
方法很简单:
n
!
=
4
!
⌊
n
5
⌋
∗
(
n
m
o
d
5
)
!
∗
⌊
n
5
⌋
!
(
m
o
d
5
)
n!=4!^{\lfloor \frac n 5\rfloor}*(n \ mod\ 5)!*\lfloor \frac n 5\rfloor!(mod\ 5)
n!=4!⌊5n⌋∗(n mod 5)!∗⌊5n⌋!(mod 5)
定义
c
n
t
(
n
)
cnt(n)
cnt(n)表示
n
!
n!
n!中所含的5的整次幂
cnt(n)=
c
n
t
(
⌊
n
5
⌋
)
+
⌊
n
5
⌋
cnt(\lfloor\frac n 5\rfloor)+\lfloor\frac n 5\rfloor
cnt(⌊5n⌋)+⌊5n⌋
这两个都是用的一个方法:把所有5的倍数和非5的倍数分开来,然后5的倍数递归处理。
现在问题就是:如何除去2相应的次幂?由于模数是 1 0 k 10^k 10k,所以2是没有逆元的。
这里可以再套用中国剩余定理:分别计算答案模 2 k 2^k 2k和 5 k 5^k 5k之后的值。然后组合起来作为 1 0 k 10^k 10k之后的答案。
显然,答案在模 2 k 2^k 2k下的答案一定为0(n极小的情况特判)
于是对模 5 k 5^k 5k,2就有逆元了,所以可以直接除去相应个数的2
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
typedef long long ll;
struct BigInteger {
typedef unsigned long long LL;
static const int BASE = 100000000;
static const int WIDTH = 8;
vector<int> s;
BigInteger& clean(){while(!s.back()&&s.size()>1)s.pop_back(); return *this;}
BigInteger(LL num = 0) {*this = num;}
BigInteger(string s) {*this = s;}
BigInteger& operator = (long long num) {
s.clear();
do {
s.push_back(num % BASE);
num /= BASE;
} while (num > 0);
return *this;
}
BigInteger& operator = (const string& str) {
s.clear();
int x, len = (str.length() - 1) / WIDTH + 1;
for (int i = 0; i < len; i++) {
int end = str.length() - i*WIDTH;
int start = max(0, end - WIDTH);
sscanf(str.substr(start,end-start).c_str(), "%d", &x);
s.push_back(x);
}
return (*this).clean();
}
BigInteger operator + (const BigInteger& b) const {
BigInteger c; c.s.clear();
for (int i = 0, g = 0; ; i++) {
if (g == 0 && i >= int(s.size()) && i >= int(b.s.size())) break;
int x = g;
if (i < int(s.size())) x += s[i];
if (i < int(b.s.size())) x += b.s[i];
c.s.push_back(x % BASE);
g = x / BASE;
}
return c;
}
BigInteger operator - (const BigInteger& b) const {
BigInteger c; c.s.clear();
for (int i = 0, g = 0; ; i++) {
if (g == 0 && i >= int(s.size()) && i >= int(b.s.size())) break;
int x = s[i] + g;
if (i < int(b.s.size())) x -= b.s[i];
if (x < 0) {g = -1; x += BASE;} else g = 0;
c.s.push_back(x);
}
return c.clean();
}
BigInteger operator * (const BigInteger& b) const {
int i, j; LL g;
vector<LL> v(s.size()+b.s.size(), 0);
BigInteger c; c.s.clear();
for(i=0;i<int(s.size());i++) for(j=0;j<int(b.s.size());j++) v[i+j]+=LL(s[i])*b.s[j];
for (i = 0, g = 0; ; i++) {
if (g ==0 && i >= int(v.size())) break;
LL x = v[i] + g;
c.s.push_back(x % BASE);
g = x / BASE;
}
return c.clean();
}
BigInteger operator / (const ll b) const {
BigInteger c;
c.s.resize(20);
ll x=0;
for(int i=int(s.size())-1;i>=0;i--){
x=x*BASE+s[i];
c.s[i]=x/b;
x=x%b;
}
return c.clean();;
}
ll operator % (const ll b) const { //???????
BigInteger c = *this;
ll m=0;
for (int i = s.size()-1; i >= 0; i--) {
m = (m*BASE + s[i])%b;
}
return m;
}
int bsearch(const BigInteger& b, const BigInteger& m) const{
int L = 0, R = BASE-1, x;
while (1) {
x = (L+R)>>1;
if (b*x<=m) {if (b*(x+1)>m) return x; else L = x;}
else R = x;
}
}
BigInteger& operator += (const BigInteger& b) {*this = *this + b; return *this;}
BigInteger& operator -= (const BigInteger& b) {*this = *this - b; return *this;}
BigInteger& operator *= (const BigInteger& b) {*this = *this * b; return *this;}
bool operator < (const BigInteger& b) const {
if (s.size() != b.s.size()) return s.size() < b.s.size();
for (int i = s.size()-1; i >= 0; i--)
if (s[i] != b.s[i]) return s[i] < b.s[i];
return false;
}
bool operator >(const BigInteger& b) const{return b < *this;}
bool operator<=(const BigInteger& b) const{return !(b < *this);}
bool operator>=(const BigInteger& b) const{return !(*this < b);}
bool operator!=(const BigInteger& b) const{return b < *this || *this < b;}
bool operator==(const BigInteger& b) const{return !(b < *this) && !(b > *this);}
};
istream& operator >> (istream& in, BigInteger& x) {
string s;
if (!(in >> s)) return in;
x = s;
return in;
}
BigInteger n;
ll ans=1;
ll mod[5]={1,10,100,1000};
ll kx[5]={1,5,25,125};
ll phi[5]={1,4,20,100};
int k;
ll res[1010],fac[1010];
ll fsp(ll x,ll y,ll Mod){
ll res=1;
while(y){
if(y&1ll)
res=res*x%Mod;
x=x*x%Mod;
y>>=1ll;
}
return res;
}
ll solve(){
BigInteger X=0;
BigInteger n1=n;
while(n1!=0){
n1=n1/5;
X+=n1;
}
int x=X%phi[k];
ll inv2=fsp(2,phi[k]-1,kx[k]);
ll inv2x=fsp(inv2,x,kx[k]);//count the 2^x mod 5^k
for(int i=1;i<mod[k];i++)
if(i%(mod[k]/kx[k])==0)
res[i%kx[k]]=i;
fac[0]=1;
for(int i=1;i<kx[k];i++){
if(i%5!=0)
fac[i]=fac[i-1]*i%kx[k];
else
fac[i]=fac[i-1];
}
ll res1=1;
while(n!=0){
BigInteger n2=n/kx[k];
ll rem1=n%kx[k];
ll rem=n2%phi[k];
res1=res1*fsp(fac[kx[k]-1],rem,kx[k])%kx[k];
res1=res1*fac[rem1]%kx[k];
n=n/5;
}
res1=res1*inv2x%kx[k];
return res[res1];
}
int main(){
freopen("num.in","r",stdin);
freopen("num.out","w",stdout);
int t;
SF("%d",&t);
while(t--){
cin>>n;
SF("%d",&k);
if(n<=20){
ll n1=n.s[0];
ll res=1;
for(ll i=1;i<=n1;i++){
res=res*i;
while(res%10ll==0)
res/=10ll;
}
ans=res%mod[k];
}
else{
ans=solve();
}
if(k==1)
PF("%01lld\n",ans);
else if(k==2)
PF("%02lld\n",ans);
else
PF("%03lld\n",ans);
}
}