Loading

[UVA12683] Odd and Even Zeroes

Description

给出 \(n\),求出 \(0!, 1!, 2! \ldots, n!\) 中有几个末尾有偶数个 \(0\)

\(1\le n\le 10^{18}\)

Solution

根据基本结论,一个数末尾 \(0\) 的个数等于该数有几个因数 \(5\)。而一个数的阶乘末尾有几个 \(0\),等于小于等于该数的所有数的因数 \(5\) 的个数的和。对此,我们设 \(d_i\) 表示有多少个数满足 \(5^i\) 是其因子,但 \(5^{i+1}\) 不是。那么考虑 \(n\) 的五进制,发现 \(d_i\) 即为对应位上的值。而 \(n!\) 末尾的 \(0\) 的个数,可以用 \(\sum\limits_{i=0}^{len} d_i\times i\) 来表示。

而我们要使其为偶数,只需要 \(i\) 是奇数的 \(d_i\) 之和为偶数即可。因此考虑数位 \(dp\),设 \(f_{i,0/1,0/1}\) 表示当前到了第 \(i\) 位,之前是否取最大值,奇数位上的和的情况(\(0\) 表示偶数,\(1\) 表示奇数)。转移就枚举当前这一位的数是什么,然后更新即可。

Code

#include<cstdio>
#include<cstring>
#define N 30
#define ll long long
using namespace std;
int len,a[N];
ll n,f[N][2][5*N];
ll dp(int x,int tp,int s)
{
    if (x<0) return s==0;
    if (f[x][tp][s]) return f[x][tp][s];
    int mx=tp?a[x]:4;ll res=0;
    for (int i=0;i<=mx;++i)
    {
        if (x&1) res+=dp(x-1,tp&&(i==mx),(s+i)%2);
        else res+=dp(x-1,tp&&(i==mx),s);
    }
    return f[x][tp][s]=res;
}
int main()
{
    while (true)
    {
        scanf("%lld",&n);
        if (n==-1) break;
        memset(f,0,sizeof(f));
        len=0;
        ll x=n;
        while (x)
        {
            a[len++]=x%5;
            x/=5;
        }
        len--;
        printf("%lld\n",dp(len,1,0));
    }
    return 0;
}
posted @ 2023-10-26 19:47  Thunder_S  阅读(3)  评论(0编辑  收藏  举报