【UVA】P12683 Odd and Even Zeroes

数位dp

写在前面的话

一拿到这个题目,第一反应是找规律,于是写个暴力怒从1打表到3000,结果发现了个循环,瞎几把搞一波,写了2000多B,一测,连样例都过不了。然后,自闭了。。
更恐怖的是,机房还有人打表掉了60多万,发现了好多好多的循环,结果卵用都没有。。。
无奈,看波题解,数位dp!!!

回归正题

推导过程

不难发现,这道题目是让我们求:
对于一个数x,设f(x)表示[0,x]含有5的因子的数的总数,
对于一个区间[1,n],求任意的x满足\(x\in [1,n]\)且f(x)%2==0的x个数
如果你这没看出来,就别往下看了

类比10进制,对于一个10进制数x,显然其可以写成:

\[x=a_0\times 10^0+a_1 \times 10^1+...+a_p \times 10^p \]

设g(x)表示[0,x]含有10的因子的数的总数,
那么,

\[g(x)=a_0 \times 0+ a_1 \times 1+ a_2\times 2+...+a_p \times p \]

类似的,对于一个5进制数y

\[y=b_0\times 5^0+b_1 \times 5^1+...+b_q \times 5^q \]

那么

\[f(y)=b_0\times 0+b_1 \times 1+...+b_q \times q \]

这样就可以数位dp了。

状态定义

\(dp_{pos,is}\)表示当前到第pos位,当前是否含有偶数个5的因子,满足条件的总数(有点拗口,实在不行可以看代码

状态转移

非常easy,见代码

代码:

#include<bits/stdc++.h>
#define int long long//日常写法,不要见怪
using namespace std;
int n,A[1000010],dp[100010][3];
int DFS(int pos,int is,bool lead,bool limit){
	if(pos==0)return !is;
	if(!lead&&!limit&&dp[pos][is]!=-1)return dp[pos][is];
	int up=limit?A[pos]:4,tmp=0;
	for(int i=0;i<=up;i++){//状态转移
		int x=(i*(pos-1))&1;
		tmp+=DFS(pos-1,(is+x)&1,lead&&(i==0),limit&&(i==up));
	}
	if(!lead&&!limit)dp[pos][is]=tmp;
	return tmp;
}
int solve(int x){
	A[0]=0;
	while(x)A[++A[0]]=x%5,x/=5;
	return DFS(A[0],0,true,true);
}
signed main(){
	memset(dp,-1,sizeof(dp));
	while(1){
		scanf("%lld",&n);
		if(n==-1)break;
		printf("%lld\n",solve(n));
	}
	return 0;
}
posted @ 2019-07-16 19:49  TieT  阅读(333)  评论(0编辑  收藏  举报