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
posted @ 2022-07-15 19:17  QuantAsk  阅读(99)  评论(1编辑  收藏  举报