题解:P10958 启示录
数位 dp 经典题,今天能在洛谷看到十分激动。
记忆化搜索实现的数位 dp 真的太好写也太好理解了吧。
代码中
可以用二分找到对应的数。
不知道多久以前写的代码,十分易懂:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int dp[20][2][2];
int a[20];
int dfs(int p,bool l2,bool l1,bool lim){
if(p==-1)return 1;
if(!lim&&dp[p][l2][l1]!=-1)return dp[p][l2][l1];
int up=lim?a[p]:9;
int s=0;
for(int i=0;i<=up;i++){
if(l2&&l1&&i==6)continue;
s+=dfs(p-1,l1,i==6,lim && i==a[p]);
}
if(!lim)return dp[p][l2][l1]=s;
return s;
}
int solve(int x){//返回 1-x 之间有几个非恶魔数
int p=0;
while(x){
a[p++]=x%10;
x/=10;
}
return dfs(p-1,0,0,1);
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);
for(int i=0;i<20;i++)for(int j=0;j<2;j++)for(int k=0;k<2;k++)dp[i][j][k]=-1;
int t;cin>>t;
while(t--){
int n;cin>>n;
int l=665,r=9999999999ll;
while(l<r){
int mid=(l+r)>>1;
if(mid+1-solve(mid)<n){
l=mid+1;
}else{
r=mid;
}
}
cout<<l<<endl;
}
return 0;
}