题意就是找0到n有多少个数中含有49。数据范围接近10^20
DP的状态是2维的dp[len][3]
dp[len][0] 代表长度为len不含49的方案数
dp[len][1] 代表长度为len不含49但是以9开头的数字的方案数
dp[len][2] 代表长度为len含有49的方案数
状态转移如下
dp[i][0] = dp[i-1][0] * 10 - dp[i-1][1]; // not include 49 如果不含49且,在前面可以填上0-9 但是要减去dp[i-1][1] 因为4会和9构成49
dp[i][1] = dp[i-1][0]; // not include 49 but starts with 9 这个直接在不含49的数上填个9就行了
dp[i][2] = dp[i-1][2] * 10 + dp[i-1][1]; // include 49 已经含有49的数可以填0-9,或者9开头的填4
接着就是从高位开始统计
在统计到某一位的时候,加上 dp[i-1][2] * digit[i] 是显然对的,因为这一位可以填 0
#include <cstdio>
#include <cstring>
#include <set>
#include <iostream>
#include <map>
#include <math.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1000005;
int t,cnt,num[25];
ll n,dp[25][3];
void init(){
dp[0][0]=1;//31行有用到,必须要对其赋值
for(int i=1;i<=20;i++){
dp[i][0]=dp[i-1][0]*10-dp[i-1][1];//每位数都要包括零在内才行
dp[i][1]=dp[i-1][0];
dp[i][2]=dp[i-1][2]*10+dp[i-1][1];
}
}
ll shuwei(){
if(n<49)
return 0;
ll ans=0;
int flag=0;
for(int i=cnt;i>0;i--){//每次循环内求得是小于当前位置上的数的情况
ans+=(ll)num[i]*dp[i-1][2];
if(flag)
ans+=(ll)num[i]*dp[i-1][0];
else{
if(num[i]>4)
ans+=dp[i-1][1];
}
if(num[i+1]==4&&num[i]==9)
flag=1;
}
return ans;
}
int main()
{
// freopen("in.txt","r",stdin);
cin>>t;
init();
while(t--){
scanf("%I64d",&n);
ll n1=n+1;//因为包括n,所以必须要n+1
cnt=0;
while(n1){
num[++cnt]=n1%10;
n1/=10;
}
num[cnt+1]=0;
ll ans=shuwei();
cout<<ans<<endl;
}
return 0;
}