这道题因为要求的是第k大的魔鬼数
所以我们可以采用二分的方法,用k-1来二分,这样就可以找到最左边的>=k的,也就是第k大
之后是数位dp,题目要求的是666,因此我们考虑用两位表示前一位填p1,前前位填p2。剩下的还要保存在这个数之前是否已经存在魔鬼数,这便与边界判断数是否成立,还有一维是老套路,看是否是最大的
#include<iostream> #include<cstring> #include<string> #include<algorithm> #include<vector> using namespace std; typedef long long ll; vector<int> num; int f[20][2][2][2][2]; int k; int dfs(int cur,int p2,int p1,int m,int flag){ if(cur==(int)num.size()) return m==1; if(f[cur][p2][p1][m][flag]!=-1) return f[cur][p2][p1][m][flag]; int v=9; if(flag) v=num[cur]; int i; long long ans=0; for(i=0;i<=v;i++){ ans+=dfs(cur+1,p1,i==6,m||(p1&&p2&&(i==6)),flag&&(i==v)); } return f[cur][p2][p1][m][flag]=ans; } int check(ll n){ num.clear(); while(n){ num.push_back(n%10); n/=10; } reverse(num.begin(),num.end()); memset(f,-1,sizeof f); int ans=dfs(0,0,0,0,1); return ans<=k; } int main(){ int t; cin>>t; while(t--){ cin>>k; k--; ll l=0,r=1e10; while(l<r){ ll mid=(l+r)>>1; if(check(mid)) l=mid+1; else r=mid; } cout<<l<<endl; } }