题目大意:
在1~n的范围内,统计含有子序列49数的个数。
看到题目很容易想到是数位dp,当时就直接搓dfs板子修改一下去找有49数的个数,但是后面TLE了,思考了很久,并且观察了自己的dfs找数过程,发现的确效率很慢,后面反过来考虑,找没有49数的个数,的确快了很多
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
using namespace std;
int a[105];
const int maxn = 1e4+5;
long long dp[20][10][20];
int len;
long long dfs(int pos,int pre,int st,int lead,int limit,int tag,int num){
if( pos > len ){
return 1;
}
if( dp[pos][pre][st]!=-1 && (!lead) && (!limit) ) return dp[pos][pre][st];
long long ret=0; int res = limit ? a[ len-pos+1 ] : 9;
for(int i = 0;i <= res; ++i ){
//前导0而且后一位也是0 那么下一层
if(pre==4 && i==9) continue;
if( (!i) && lead ) ret += dfs (pos+1,i,st,1,i==res && limit,tag,num*10+i);
//前导0结束
else if( i && lead ) ret += dfs(pos+1,i,st+1,0,i==res && limit,tag,num*10+i);
else ret += dfs(pos+1,i,st+1,0,i==res && limit,tag,num*10+i);
}
if((!lead) && (!limit)){
dp[pos][pre][st] = ret;
}
return ret;
}
long long div_num(long long x){
len = 0;
while( x ){ a[++len] = x%10; x/=10; }
memset(dp,-1,sizeof(dp));
return dfs(1,0,0,1,1,0,0);
}
long long read(){
char ch = getchar(); int f = 1;
long long ans = 0;
while(ch < '0' || ch >'9'){
if(ch=='-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
ans = ans*10 + ch - '0';
ch = getchar();
}
return ans*f;
}
char str[25];
void quick_print(long long x){
int tot = 0;
while(x){
str[tot++] = x%10 + '0';
x/=10;
}
for(int i = tot-1; i >= 0; --i){
putchar(str[i]);;
}
putchar('\n');
}
int main()
{
int T; cin >> T;
while(T--){
long long x; x = read();
cout << x - (div_num(x) - div_num(0))<< endl;
}
return 0;
}