题意:给定一个数n,判定0! , 1! , 2!, ... , n!这(n+1)个阶乘有多少个末尾0的个数为偶数。 (0<=n<=10^18)
思路:i!末尾0个数取决于阶乘中5的个数。我们以5个数为一个整体。
1(5) 1(10) 1(15) 1(20) 2(25) 1(30) 1(35) 1(40) 1(45) 2(50) 1(55) 1(60) 1(65) 1(70) 2(75) 1(80) 1(85) 1(90) 1(95) 2(100) 1(105) 1(110)
1(115) 1(120) 3(125) 前一个数代表这个数中5的幂,后一个代表那个数。
我们将625以前的数分组如下:
1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 3
1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 3
1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 3
1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 3
1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 4
那么哪些段是满足末尾0的个数为偶数呢? 我们将第一行的段表示出来,(y)为是,(n)为否
(y)1 (n) 1 (y) 1 (n) 1 (y) 2 (y) 1 (n) 1 (y) 1 (n) 1 (y) 2 (y) 1 (n) 1 (y) 1 (n) 1 (y) 2 (y) 1 (n) 1 (y) 1 (n) 1 (y) 2 (y) 1 (n) 1 (y) 1 (n) 1 (y) 3
我们发现:
(1)已知直到出现第一个5的k次幂的大段,那么直到出现第一个5的(k+1)次幂的大段一定是5的k次幂的大段重复5段,且最后一段的最后
一个元素把k换成(k+1)即为直到出现第一个5的(k+1)次幂的大段。
(2)已知直到出现第一个5的k次幂的大段中每个小段的y/n情况,那么可以推知第一个5的(k+1)次幂的大段的每个小段的y/n情况。
1.如果k是偶数,那么接下来的4个段与之前的段情况完全相同。
2.如果k是奇数,那么接下来的4段中,第2和第4段与之前段的情况相同。第1和第3段与之前段的情况正好相反。
设num[ i ]表示分组后直到出现第一个5的i次幂时,之前满足末尾0的个数为偶数的段的个数。
那么
num[ i ]=5 * num[ i - 1] i为奇数;
num[ i ]=3 * num[ i - 1 ]+2 * ( a[ i - 2 ] - num[ i - 1 ] ) i为偶数;
预处理完num数组之后,对于n,我们每次二分找不大于n的最大次幂区间, 不妨设为k,x= n / a[ k ],那么n -= a[ k ] * x; 同时根据k的奇
偶性,更新ans。同时我们设了一个变量flag记录当前的翻转状态,flag=0表示剩余区间也是先从y开始的,即与num数组的状态一
致;flag=1表示剩余区间是从n开始的。每次更新完ans,同时根据k看是否需要翻转,详见代码:
// file name: LA6575.cpp //
// author: kereo //
// create time: 2014年10月08日 星期三 20时13分18秒 //
//***********************************//
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=30;
const int inf=0x3fffffff;
const int mod=1000000000+7;
#define L(x) (x<<1)
#define R(x) (x<<1|1)
ll n;
ll num[MAXN];
ll a[MAXN];
int main()
{
num[1]=1; num[2]=3;
a[0]=1;
for(int i=1;i<MAXN;i++)
a[i]=a[i-1]*5;
for(int i=3;i<MAXN;i++){
if(i%2)
num[i]=num[i-1]*5;
else
num[i]=3*num[i-1]+2*(a[i-2]-num[i-1]);
}
while(~scanf("%I64d",&n) && n!=-1){
ll ans=0;
int flag=0; //flag=0表示1 0 1 0 1的形式,flag=1表示0 1 0 1 0的形式
if(n <= 4){ //特判下不能构成段的情况。
cout<<n+1<<endl;
continue;
}
n++;
while(n){
int k=upper_bound(a,a+MAXN,n)-a;
k-=1;
if(k == 0){
if(flag == 0)
ans+=n;
break;
}
ll x=n/a[k];
n-=a[k]*x;
if(k&1){
if(x%2){
int a1=x/2,a2=(x+1)/2;
if(!flag)
ans+=a2*num[k]*5+a1*(a[k-1]-num[k])*5;
else
ans+=a1*num[k]*5+a2*(a[k-1]-num[k])*5;
}
else
ans+=x/2*a[k-1]*5;
}
else{
if(flag == 0)
ans+=num[k]*x*5;
else
ans+=(a[k-1]-num[k])*x*5;
}
if(k&1){
if(x%2)
flag^=1;
}
}
cout<<ans<<endl;
}
return 0;
}