正题
题目链接:http://pjudge.ac/problem/21652
题目大意
给出一个正整数kkk,求第nnn个xxx满足x×(10k−1)x\times (10^k-1)x×(10k−1)中没有一个数位为999。
1≤n≤1018,1≤k≤181\leq n\leq 10^{18},1\leq k\leq 181≤n≤1018,1≤k≤18
解题思路
首先是从高位到低位逐步确定答案,但是直接暴力算乘法肯定很麻烦,我们考虑反过来做。
我们算第nnn个合法的x×(10k−1)x\times (10^k-1)x×(10k−1),然后再除以10k−110^k-110k−1。
那么现在问题就是给定一些确定的位,求剩下的位有多少种不含999的组法能够拼出10k−110^k-110k−1的倍数。
然后一个数10k−110^k-110k−1的倍数的条件就是将它每kkk位分割一次提出来求和,如果是10k−110^k-110k−1的倍数就合法。
段数不会太多,我们可以暴力枚举10k−110^k-110k−1的倍数,然后数位dp求解。
细节有点多。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define ll __int128
using namespace std;
const int W=40;
int k,a[80],b[80];
ll n,f[80][10];
ll read(){ll x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}
void print(ll x)
{if(x>9)print(x/10);putchar(x%10+'0');return;}
ll solve(){int L=(W+k-1)/k;ll pw=1,ans=0;for(int i=1;i<=k;i++)pw=pw*(ll)10;pw--;for(int d=1;d<=L;d++){ll p=d*pw;for(int i=0;i<k;i++)b[i]=p%10,p/=10;memset(f,0,sizeof(f));f[0][0]=1;for(int i=0;i<k;i++){for(int z=0;z<L;z++)for(int x=(z==0);x<10;x++)f[z/10][z%10]+=f[z][x],f[z][x]=0;for(int j=0;j<L;j++)for(int z=L-1;z>=0;z--)for(int x=9;x>=0;x--){if(!f[z][x])continue;ll r=f[z][x];f[z][x]=0;if(a[j*k+i]==-1)for(int y=8;y>=0;y--)f[z+(x+y)/10][(x+y)%10]+=r;else f[z+(x+a[j*k+i])/10][(x+a[j*k+i])%10]+=r;}for(int z=0;z<L;z++)for(int x=0;x<10;x++)if(x!=b[i])f[z][x]=0;}for(int x=0;x<10;x++)ans+=f[p][x];}return ans;
}
signed main()
{k=read();n=read();ll now=0,las;int m=(W+k-1)/k*k;for(int i=0;i<m;i++)a[i]=-1;for(int i=m-1;i>=0;i--){a[i]=0;now+=(las=solve());while(now<n){a[i]++;now+=(las=solve());}now-=las;}ll ans=0;//print(now);for(int i=m-1,flag=0;i>=0;i--)ans=ans*(ll)10+a[i];ll pw=1;for(int i=0;i<k;i++)pw=pw*(ll)10;pw=pw-1;ans/=pw;print(ans);return 0;
}
//1 8