POJ 1150 The Last Non-zero Digit

問題

nPmの、末尾の0を全て取り除いた時の下1桁を求めよ。

制約条件

n,m≦20000000

方針

n!をa * p^e(ただしpは素数)として求めることは可能(蟻本の解説など参照)

n!とm!について、mod 2とmod 5でaおよびeを計算してやり、
10で割れるだけ割れば、答えのmod 2とmod 5が求まり、そこから答えのmod 10が求まる。

ソースコード

int fact2[2],fact5[5];

int mod_fact(int n,int p,int &e){
  e=0;
  if(n==0)return 1;
  int res=mod_fact(n/p,p,e);
  e+=n/p;
  if(n/p%2!=0)return res*(p-(p==2?fact2[n%p]:fact5[n%p]))%p;
  return res*(p==2?fact2[n%p]:fact5[n%p])%p;
}
int extgcd(int a,int b,int &x,int &y){
  int d=a;
  if(b!=0){
    d=extgcd(b,a%b,y,x);
    y-=a/b*x;
  }
  else x=1, y=0;
  return d;
}
int mod_inv(int n,int m){
  int x,y;
  extgcd(n,m,x,y);
  return (x%m+m)%m;
}
int mod_pow(int n,int m,int p){
  int res=1;
  for(;m;m/=2){
    if(m&1)res=res*n%p;
    n=n*n%p;
  }
  return res;
}
int main(){
  fact2[0]=fact2[1]=1;
  fact5[0]=1;
  rep(i,4)fact5[i+1]=fact5[i]*(i+1)%5;
  
  int n,m;
  while(~scanf("%d%d",&n,&m)){
    m=n-m;
    int a5,e5,a2,e2;
    int b5,f5,b2,f2;
    a5=mod_fact(n,5,e5);
    b5=mod_fact(m,5,f5);
    a2=mod_fact(n,2,e2);
    b2=mod_fact(m,2,f2);
    
    e5-=f5;
    e2-=f2;
    int x=min(e5,e2),mod5,mod2;
    if(e5-x>0)mod5=0;
    else mod5=a5*mod_inv(b5,5)*mod_inv(mod_pow(2,x,5),5)%5;
    if(e2-x>0)mod2=0;
    else mod2=a2*mod_inv(b2,2)%2;
    
    rep(i,10)if(i%2==mod2&&i%5==mod5){
      printf("%d\n",i);
    }
  } 
  return 0;
}