您的位置:首页 > 运维架构

LA6575 Odd and Even Zeroes (2013-2014 ACM-ICPC Southwestern Europe Regional Contest F题) 规律

2014-10-09 16:48 447 查看
题意:给定一个数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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐