首页 > 代码库 > UVa 12683 Odd and Even Zeroes(数论+数位DP)

UVa 12683 Odd and Even Zeroes(数论+数位DP)

题意: 问 小于等于n的数中 (0<=n <= 1e18)有多少个的阶乘的尾数0的个数为偶数

思路:暴力肯定不行,那么从数论出发,要使尾数0的个数为偶数,那么就要使N的阶乘5的幂为偶数,(二的指数肯定小于等于5的指数),求N!的阶乘5的指数就是N/5+ N/25+N/125。。。。

以530为例,尝试着将转化为5进制  即为 4110,那么5的指数 就是 411+41+4 (5进制)容易发现要使这个数是偶数,就是要使5进制奇数位的个数为偶数,根据刚才那个加法,可以看出,最后结果的5进制的末位就是411+41+4 的末位 即 4+1+1 即为原数最高位到最低第二位每一位的和,末二位依次类推。。。。。

那么只要做到这里。。。接下来就是数位DP的事了,DP[pos][parity][presum]


#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <algorithm>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long LL;
vector<int> digit;
#define REP(_,a,b) for(int _ = (a); _ <= (b); _++)
LL dp[30][2][2];
LL n;
LL dfs(int pos,int parity,int sum,bool done) {
    if(pos==0) {
        if(parity==0) {
            int sz = done?digit[pos]:4;
            return sz+1;
        }else{
            return 0;
        }

    }
    if(!done && dp[pos][parity][sum] != -1) return dp[pos][parity][sum];
    LL ret = 0;
    int end = done? digit[pos]:4;
    for(int i = 0; i <= end; i++) {
        ret += dfs(pos-1,(parity+(sum+i)&1)&1,(sum+i)&1,done&&i==end ) ;
    }
    if(!done) dp[pos][parity][sum] = ret;
    return ret;
}
LL solve(LL n) {
    if(n <= 4) return n+1;
    memset(dp,-1,sizeof dp);
    digit.clear();
    while(n) {
        digit.push_back(n%5);
        n /= 5;
    }
    return dfs(digit.size()-1,0,0,1);
}
int main(){

    while(~scanf("%lld",&n) && n !=-1) {
        printf("%lld\n",solve(n));
    }
    return 0;
}


UVa 12683 Odd and Even Zeroes(数论+数位DP)