Odd and Even Zeroes (UVA 12683) (数位DP).

题面:


给一个 n. 求[0,n] 有多少个数满足其阶乘的末尾上 有偶数个0.

n∈ [1,1e18].


题意 极为简洁.

正解也极为有意思(并非找规律).

重点: 如何将问题 与 数位DP相关.

将整个问题 分成小问题的话.

就是 当一个数满足 题设条件时. 它具有怎样的性质.

然后 就是去构造这个数.

一个数的阶乘有多少个 0 就是 阶乘的因子中有多少个5.

先看看如果是暴力的话怎么写.

int ans = 0;
for(int i=1;i<=n;i++) {
    int he = 0,x;
    for(int j=5;j<=i;j*=5) {
        x = i/j;
        he += x;
    }
    if(he%2 == 0) ans ++;
}

可以看一下 内层循环(j) 在五进制下是如何执行的.

假设当前数 i 的五进制为 ABCDE.

i = A *(5^4) + B * (5^3) + C * (5^2) + D * (5^1) + E * (5^0).

当 j = 5时.x = i/5 = ABCD. 类似二进制. 即将该五进制整体右移一位.

类似的

每一次的 x.

分别是.

ABCD. (j=5).

ABC. (j=25).

AB. (j=125).

A. (j=625).

此时 A 对于 he 的贡献 为 A*(625 + 125 + 25 + 5).

因为题目 只需要判断 he 的奇偶性.

那就看看 A贡献的奇偶性.

它分别受到 A本身的奇偶性 和 后面项数的奇偶性的影响.

因为 5的幂次方 必是奇数 所以只需要关心后面的项数.

即 A * (pos_A-1)的奇偶性. (pos_A为A所在5进制的位置).

类似的每个五进制位 都有其对应的贡献.

此时 这个数的he的奇偶性 已经和它本身的 5进制 有关系了.

那么 将题目再一次转化为:

在[0,n]的区间里 满足某个性质的 数字个数.

OK. 这样就和学过的数位DP挂上钩了.

该性质 即为 在该数的五进制上

(∑每一个数位的系数*(位数-1)) 为偶数.

数位DP就应该比较好写了.

#include <cstdio>
#include <cstring>
#define LL long long
LL dp[33][2],num[33],cot;
LL dfs(int pos,bool limit,bool lead,int pre) {
	if(!pos) return !pre;
	if(!limit && lead && ~dp[pos][pre]) return dp[pos][pre];
	LL up = limit? num[pos]:4,res = 0;
	for(int i=0; i<=up; i++) {
		if(!lead) res += dfs(pos-1,limit && i == up,lead||i,(pre+i*(pos-1))&1);
		else res += dfs(pos-1,limit && i == up,lead,(pre+i*(pos-1))&1);
	}
	if(!limit && lead) dp[pos][pre] = res;
	return res;
}
LL solve(LL x) {
	cot = 0;
	while(x) num[++cot] = x%5,x /= 5; 
	return dfs(cot,1,0,0);
}
int main() {
	memset(dp,-1,sizeof(dp));
	LL n;
	while(scanf("%lld",&n) && ~n) printf("%lld\n",solve(n));
	return 0;
}

总结: 从题目发现了问题本身具有的性质. 然后利用这个性质 进行的 利用所学知识去求解.

利用五进制 得出符合条件数字的性质 也是这个题目最难也最有意思的部分.

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值