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

来源:互联网 发布:义乌司法拍卖淘宝网站 编辑:程序博客网 时间:2024/06/15 19:36

题意:给定一个数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;elsenum[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;elseans+=a1*num[k]*5+a2*(a[k-1]-num[k])*5;}elseans+=x/2*a[k-1]*5;}else{if(flag == 0)ans+=num[k]*x*5;elseans+=(a[k-1]-num[k])*x*5;}if(k&1){if(x%2)flag^=1;}}cout<<ans<<endl;}return 0;}


0 0
原创粉丝点击