数位dp
写在前面的话
一拿到这个题目,第一反应是找规律,于是写个暴力怒从1打表到3000,结果发现了个循环,瞎几把搞一波,写了2000多B,一测,连样例都过不了。然后,自闭了。。
更恐怖的是,机房还有人打表掉了60多万,发现了好多好多的循环,结果卵用都没有。。。
无奈,看波题解,数位dp!!!
回归正题
推导过程
不难发现,这道题目是让我们求:
对于一个数x,设f(x)表示[0,x]含有5的因子的数的总数,
对于一个区间[1,n],求任意的x满足(xin [1,n])且f(x)%2==0的x个数
(如果你这没看出来,就别往下看了)
类比10进制,对于一个10进制数x,显然其可以写成:
[x=a_0 imes 10^0+a_1 imes 10^1+...+a_p imes 10^p
]
设g(x)表示[0,x]含有10的因子的数的总数,
那么,
[g(x)=a_0 imes 0+ a_1 imes 1+ a_2 imes 2+...+a_p imes p
]
类似的,对于一个5进制数y
[y=b_0 imes 5^0+b_1 imes 5^1+...+b_q imes 5^q
]
那么
[f(y)=b_0 imes 0+b_1 imes 1+...+b_q imes 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
",solve(n));
}
return 0;
}