题目:http://acm.hdu.edu.cn/showproblem.php?pid=3555
题意:求小于等于N(N可以用64位整形表示)的数中有多少个含有49
分析:之前做过hdoj上的《不要62》,所以这题一看就可以用数位dp搞,不过看discuss中有人说可以推公式搞,也想了下没搞定。。
直接求有多少个含有‘49’的数,不好初始化,所以反过来求有多少个数不含‘49’,这样对整个64位非负整数区间做初始化。现在只要按照输入的n进行dp,
dp方法是从高位到低位进行(防止溢出N),对每一位都加上该位取小于N的该位时的值,然后在加上当该位取的数等于N的那一位时的值。
前面的值可以直接算,后面的值就是下位在其前一位确定时的值。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
typedef __int64 ll;
ll dp[20][2];
void init()
{
dp[0][0]=1;dp[0][1]=0;
dp[1][0]=9;dp[1][1]=1;
for(int i=2;i<20;i++)
{
dp[i][0]=9*(dp[i-1][0]+dp[i-1][1])-dp[i-1][1];
dp[i][1]=dp[i-1][0]+dp[i-1][1];
}
}
ll get(ll x)
{
bool flag=false;
int sc=0,nums[19];
while(x>0){nums[sc++]=x%10;x/=10;}
ll r=0;
for(int i=sc;i>0;i--)
{
int num=nums[i-1];
if(num<=4)r+=num*(dp[i-1][0]+dp[i-1][1]);
else r+=(num*(dp[i-1][0]+dp[i-1][1])-dp[i-1][1]);
if(flag&&num==9)break;
if(num==4)flag=true;
else flag=false;
}
return r;
}
int main()
{
init();
int t;scanf("%d",&t);
while(t--)
{
ll n;scanf("%I64d",&n);
if(n<49)printf("0\n");
else printf("%I64d\n",(n+1)-get(n+1));
}
return 0;
}