qoj1875 Nein 一个比较详细的题解

题意

给定 \(k\)\(n\),输出第 \(n\) 小的正整数 \(x\) 使得 \(x \times (10^k - 1)\) 不包含任何 \(9\)

\(1 \le k \le 18\)\(1 \le n \le 10^{18}\)

题解

\(M = 10^k - 1\)

直接数并判断不包含 \(9\) 是较为困难的,我们考虑直接去数所有不包含 \(9\) 的数中第 \(n\) 小的 \(M\) 的倍数,最后将答案除以 \(M\)

我们发现,对于一个形如 \(10^k - 1\) 的数的倍数,一定满足从低到高位,每 \(k\) 位分成一段,每一段的和也是 \(10^k - 1\) 的倍数。

这样有一个好的性质:设这个数会分成 \(B\) 段,则每一段加起来的和一定是 \(M, 2M, 3M, \cdots, BM\) 之中的一个。这样的数不会太多。

那我们就可以数位 dp 了。考虑固定一个前缀(从高到低位的),求出后面有多少种填法使得这个数是 \(M\) 的倍数(具体来说,每次在最高的那个固定一个 \([0, 8]\) 的数并计算后缀的填法数,如之前的总方案数加上现在的总方案数 \(\ge n\),则往下移动一位,否则把总方案数加上目前的后缀填法数并将这一位的值 \(+ 1\))。

显然,后缀的每一段加起来的和一定是 \(r, r + M, r + 2M, \cdots, r + BM\) 的形式。由于 \(B\) 很小,则我们可以直接枚举某一个和并且进行计数。

\(dp_{i, j}\) 表示现在已经确定了每一段的最低的 \(i\) 位,且目前这一位向上进了 \(j\) 位的方案数,则有转移:

\[dp_{i, j} = \sum\limits_{k = 0}^B dp_{i - 1, k} \times G_{B - [i > L], 10j + sum_i - k} \]

其中:

  • \(L\) 为后面没有固定的最左边的那一段的长度(可能小于 \(k\))。
  • \(G_{i, j}\)\(i\) 个在 \([0, 8]\) 内的数加起来的和为 \(j\) 的方案数(有顺序的区别),可以提前预处理。

转移式为 \(G_{i, j} = \sum\limits_{k = 0}^{\min(j, 8)} G_{i - 1, j - k}\),初始值为 \(G_{0, 0} = 1\)

  • \(sum_i\) 为目前选取的和从低到高的第 \(i\) 位。

最终答案存储在 \(dp_{k, 0}\) 里,初始值为 \(dp_{0, 0} = 1\),可能有一些边界情况要处理。

状态数是 \(\mathcal{O}(kB)\) 的,单次转移是 \(\mathcal{O}(B)\) 的,\(sum\) 的取值共有 \(\mathcal{O}(B)\) 种,枚举前缀的复杂度是 \(\mathcal{O}(9kB)\) 的,预处理的复杂度可以忽略不计。总复杂度即为 \(\mathcal{O}(9k^2B^4)\) 的。

在本题的数据范围下,__int128 是完全可以存的下的,也就代表说位数,也就是 \(kB\),最多是 \(36\),而且根本跑不满,所以肯定可以过。

代码

有一些细节要注意,具体见代码。

#include<bits/stdc++.h>
using namespace std;
#define i128 __int128
#define ull unsigned long long
#define int long long
#define fo(i,l,r) for(int i=l;i<=r;i++)
#define fd(i,r,l) for(int i=r;i>=l;i--)
#define sqrt __builtin_sqrt
#define pii pair<int,int>
#define pll pair<long long,long long>
#define pdd pair<double,double>
#define tiii tuple<int,int,int>
#define fi first
#define se second
#define mem(a,b) memset(a,b,sizeof a)
#define Gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,__SIZE,stdin),(iS==iT?EOF:*iS++)):*iS++)
namespace io{const int __SIZE=(1<<21)+1;char ibuf[__SIZE],*iS,*iT,obuf[__SIZE],*oS=obuf,*oT=oS+__SIZE-1,__c,qu[55];int __f,qr,_eof;inline void flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}inline void gc(char&x){x=Gc();}inline void pc(char x){*oS++=x;if(oS==oT)flush();}inline void pstr(const char*s){int __len=strlen(s);for(__f=0;__f<__len;++__f)pc(s[__f]);}inline void gstr(char *s){for(__c=Gc();__c<32||__c>126||__c==' ';)__c=Gc();for(;__c>31&&__c<127&&__c!=' '&&__c!='\n'&&__c!='\r';++s,__c=Gc())*s=__c;*s=0;}template<class I>inline bool gi(I&x){_eof=0;for(__f=1,__c=Gc();(__c<'0'||__c>'9')&&!_eof;__c=Gc()){if(__c=='-')__f=-1;_eof|=__c==EOF;}for(x=0;__c<='9'&&__c>='0'&&!_eof;__c=Gc())x=x*10+(__c&15),_eof|=__c==EOF;x*=__f;return!_eof;}template<class I>inline void print(I x){if(!x)pc('0');if(x<0)pc('-'),x=-x;while(x)qu[++qr]=x%10+'0',x/=10;while(qr)pc(qu[qr--]);}struct Flusher_{~Flusher_(){flush();}}io_flusher_;}using io::pc;using io::gc;using io::pstr;using io::gstr;using io::gi;using io::print;
const int mod=998244353;
template<typename T>inline void cmax(T&x,T y){x=max(x,y);}
template<typename T>inline void cmin(T&x,T y){x=min(x,y);}
template<typename T>inline T fplus(T x,T y){return x+y-(x+y>=mod?mod:0);}
template<typename T>inline T fminus(T x,T y){return x-y+(x-y<0?mod:0);}
template<typename T>inline void cplus(T&x,T y){x=fplus(x,y);}
template<typename T>inline void cminus(T&x,T y){x=fminus(x,y);}
template<typename T>inline T qp(T x,T y){T ans=1;while(y){if(y&1)ans=ans*x%mod;x=x*x%mod,y>>=1;}return ans;}
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
inline int rdint(int l,int r){return uniform_int_distribution<>(l,r)(rnd);}
inline double rddbl(double l,double r){return uniform_real_distribution<>(l,r)(rnd);}
#ifdef QWERTIM
namespace __DEBUG_UTIL__{template<typename T>concept is_iterable=requires(T&&x){begin(x);}&&!is_same_v<remove_cvref_t<T>,string>;void print(const char*x){cerr<<x;}void print(char x){cerr<<"\'"<<x<<"\'";}void print(bool x){cerr<<(x?"T":"F");}void print(string x){cerr<<"\""<<x<<"\"";}void print(vector<bool>&&v){int f=0;cerr<<'{';for(auto&&i:v)cerr<<(f++?",":"")<<(i?"T":"F");cerr<<"}";}template<typename T>void print(T&&x){if constexpr(is_iterable<T>)if(size(x)&&is_iterable<decltype(*(begin(x)))>){int f=0;cerr<<"\n~~~~~\n";for(auto&&i:x)cerr<<setw(2)<<left<<f++,print(i),cerr<<"\n";cerr<<"~~~~~\n";}else{int f=0;cerr<<"{";for(auto&&i:x)cerr<<(f++?",":""),print(i);cerr<<"}";}else if constexpr(requires{x.pop();}){auto temp=x;int f=0;cerr<<"{";if constexpr(requires{x.top();})while(!temp.empty())cerr<<(f++?",":""),print(temp.top()),temp.pop();else while(!temp.empty())cerr<<(f++?",":""),print(temp.front()),temp.pop();cerr<<"}";}else if constexpr(requires{x.first;x.second;})cerr<<'(',print(x.first),cerr<<',',print(x.second),cerr<<')';else if constexpr(requires{get<0>(x);}){int f=0;cerr<<'(',apply([&f](auto...args){((cerr<<(f++?",":""),print(args)),...);},x),cerr<<')';}else cerr<<x;}template<typename T,typename...V>void printer(const char*names,T&&head,V&&...tail){int i=0;for(size_t bracket=0;names[i]!='\0'&&(names[i]!=','||bracket!=0);i++)if(names[i]=='('||names[i]=='<'||names[i]=='{')bracket++;else if(names[i]==')'||names[i]=='>'||names[i]=='}')bracket--;cerr.write(names,i)<<" = ",print(head);if constexpr(sizeof...(tail))cerr<<" ||",printer(names+i+1,tail...);else cerr<<"]\n";}template<typename T,typename...V>void printerArr(const char*names,T arr[],size_t N,V...tail){size_t i=0;for(;names[i]&&names[i]!=',';i++)cerr<<names[i];for(i++;names[i]&&names[i]!=',';i++);cerr<<" = {";for(size_t ind=0;ind<N;ind++)cerr<<(ind?",":""),print(arr[ind]);cerr<<"}";if constexpr(sizeof...(tail))cerr<<" ||",printerArr(names+i+1,tail...);else cerr<<"]\n";}}
#define debug(...) cerr<<'\n'<<__LINE__<<": [",__DEBUG_UTIL__::printer(#__VA_ARGS__,__VA_ARGS__)
#define debugArr(...) cerr<<'\n'<<__LINE__<<": [",__DEBUG_UTIL__::printerArr(#__VA_ARGS__,__VA_ARGS__)
#else
#define debug(...)
#define debugArr(...)
#endif
int n,k,M,B;
i128 G[105][805],pw[37],ans,tot,dp[55][55];
void init(){
	G[0][0]=pw[0]=1;
	fo(i,1,37)fo(j,0,8*i)fo(k,0,min(j,8ll))G[i][j]+=G[i-1][j-k];
	fo(i,1,37)pw[i]=pw[i-1]*10;
	M=pw[k]-1;
}
bool c9(i128 x){return !x?1:(x%10==9?0:c9(x/10));}
i128 calc(int len,i128 sum){
	if(!len)return !sum;
	if(len<=k)return sum<pw[len]&&c9(sum);
	mem(dp,0),dp[0][0]=1;
	int L=(len-1)%k+1;
	i128 sb=sum;
	fo(i,1,k){
		fo(j,0,B)fo(K,0,min((i128)B,10*j+sum%10))dp[i][j]+=dp[i-1][K]*G[B-(i>L)][10*j+sum%10-K];
		sum/=10;
	}
	return dp[k][sum];
}
void solve(){
	gi(k),gi(n),init();
	fd(i,37,0)fo(j,0,8){
		B=(i+k-1)/k;
		i128 r=((i128)M-ans%M)%M,tmp=-(!ans);
		fo(j,0,B)tmp+=calc(i,r+M*j);
		if(tot+tmp>=n)break;
		tot+=tmp,ans+=pw[i];
	}
	print(ans/M);
}
signed main(){
	int T=1;
	// cin>>T;
	while(T--)solve();
	return 0;
}

跑的飞快,只有 2ms

posted @ 2025-11-17 10:42  qwertim  阅读(3)  评论(0)    收藏  举报