pjudge#21652-[PR #4]到底有没有九【数位dp】
正题
题目链接:http://pjudge.ac/problem/21652
题目大意
给出一个正整数\(k\),求第\(n\)个\(x\)满足\(x\times (10^k-1)\)中没有一个数位为\(9\)。
\(1\leq n\leq 10^{18},1\leq k\leq 18\)
解题思路
首先是从高位到低位逐步确定答案,但是直接暴力算乘法肯定很麻烦,我们考虑反过来做。
我们算第\(n\)个合法的\(x\times (10^k-1)\),然后再除以\(10^k-1\)。
那么现在问题就是给定一些确定的位,求剩下的位有多少种不含\(9\)的组法能够拼出\(10^k-1\)的倍数。
然后一个数\(10^k-1\)的倍数的条件就是将它每\(k\)位分割一次提出来求和,如果是\(10^k-1\)的倍数就合法。
段数不会太多,我们可以暴力枚举\(10^k-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