题目链接
题目大意
规定f(d,n)=x的含义为从1到n之间的数,每个数位上出现d的次数
我们说f(d,n)=n是一个good number ,给定d和x找出小于等于x的最大的good number
解题思路
我们先算出来f(d,x)=y,因为每个数上的最多有18位,所以我们每次减少(abs(x-y))/18.
算f(d,x)的时候我们用数位dp算;
我们设现在位置是第pos位,那么比pos位小的就有(pos-1)*10pos-2种情况(固定pos-1位的其中一位为d,其他位的情况为10 * pow-2),当当前位置为上届情况时,比如423,当我们枚举到第三位4的时候当d=4,那么所有的情况就只有23种(因为4是固定的)
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=50;
long long a[N],jie[N],num[N],ans;
int d;
long long n;
long long jisuan(int pos)
{
if(pos<=0)
return 0;
return (long long)pos*jie[pos-1];
}
void dfs(int pos)
{
if(pos==0)
return ;
for(int i=0; i<a[pos]; i++)
{
if(i==d)
ans+=jie[pos-1];
ans+=jisuan(pos-1);
}
if(a[pos]==d)
ans+=num[pos-1]+1;//上界情况
dfs(pos-1);
}
long long F(long long x)
{
int cnt=0;
ans=0;
num[0]=0;
while(x)
{
a[++cnt]=x%10;
x/=10;
}
for(int i=1;i<=cnt;i++)
num[i]=num[i-1]+a[i]*jie[i-1];
dfs(cnt);
}
long long solve(long long x)
{
jie[0]=1;
for(int i=1; i<=19; i++)
jie[i]=jie[i-1]*(long long)10;
F(x);
while(ans!=x)
{
x-=(abs(x-ans)+(long long)17)/(long long)18;
F(x);
}
return x;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %lld",&d,&n);
printf("%lld\n",solve(n));
}
return 0;
}