题解:qoj1875 Nein

题意:给出 \(k,n\),问第 \(n\) 个是 \(10^k-1\) 的倍数的且每个数字不含有 \(9\) 的数是多少。\(k\le 18,n\le 10^{18}\)

做法:

首先先跳出一个很显然的想法,一开始看这个东西觉得应该是拆成 \(x10^k-x\) 去讨论,但是显然有点完蛋。所以考虑去对 \(x(10^k-1)\) 直接枚举去计算。

我们猜答案位数不会太大,取大概 \(20+\) 位即可。

那么现在考虑类似数位 dp,每次枚举还没确定的最高位是多少然后去看有多少个数满足。因为是 \(10^k-1\) 的倍数的条件是 \(k\) 位一段,段和加起来是其倍数,所以我们考虑直接把 \(\bmod k\) 相等的位全部加在一起,也就是把 \(10^x\) 的位权变成 \(10^{x\bmod k}\)。但是有个比较麻烦的是我们需要同时在十进制和 \(10^k-1\) 的倍数这个限制考虑,因为答案位数不大,所以其实我们可以直接枚举加和是多少倍的 \(10^k-1\),我们就可以求出来我们最后需要每个十进制位上是多少。

然后考虑 dp,从低位往高位 dp,\(dp_{i,j}\) 代表考虑到了第 \(i\) 位,目前被进了 \(j\) 位到第 \(i\) 位上。因为我们确定了总位数,所以可以知道有多少个数会贡献在 \(i\) 这一位,转移直接枚举他们的总和,再计算进位个数就可以转移,记得需要满足我枚举的数的十进制位要求。注意到这个东西和是第多少位没啥关系,所以我们可以提前处理出来 \(f_{i,j}\) 代表 \(i\)\([0,8]\) 的数和为 \(j\) 的方案数。

然后就做完了,感觉这个题的唯一脑筋急转弯在于枚举 \(x\times (10^k-1)\)

输出就是做一个高精除就可以了。

代码实现上,我 dp 为了方便用的是记忆化写法,记得多开点 __int128,这个题到处都会爆 long long

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 55, B = 20;
__int128 dp[maxn][maxn * maxn];
int n, k, t, a[maxn];
__int128 f[maxn][maxn * maxn], all, tc;
int lim[maxn * maxn], vis[maxn][maxn * maxn], ad[maxn * maxn], to[maxn * maxn], ed;
__int128 cal(int x, int add) {
	if(x >= ed)
		return (add == 0);
	if(vis[x][add])
		return f[x][add];
	vis[x][add] = 1, f[x][add] = 0;
	for (int i = (to[x] - (ad[x] + add) % 10 + 10) % 10; i + ad[x] + add <= 54 * 8; i += 10)
		if(dp[lim[x]][i])
			f[x][add] = (f[x][add] + cal(x + 1, (i + ad[x] + add) / 10) * dp[lim[x]][i]);
//	if(ed == 2 && a[3] == 1)
//		cout << x << " zcmvsabviwqu" << add + ad[x] << " " << (int)f[x][add] << endl;
	return f[x][add];
}
void solve(int x) {
	all = 0;
	for (__int128 nw = 1; nw <= ((B) / k + 2); nw++) {
		ed = 0;
		__int128 tmp = nw * tc;
		while(tmp) {
			to[ed++] = tmp % 10;
			tmp /= 10;
		}
		for (int i = 0; i < k; i++)
			lim[i] = 0, ad[i] = 0;
		for (int i = 0; i < (B / k + 2) * k; i++)
			lim[i % k]++;
		for (int i = (B / k + 2) * k - 1; i >= x; i--) {
			lim[i % k]--;
			//	if(nw <= 2)
			//	cout << i << " " << (int)(all) << endl;
			ad[i % k] += a[i];
		}
//		cout << (int)(all) << " " << (int)nw << endl;
		memset(vis, 0, sizeof(vis));
		all += cal(0, 0);
	}
}
void write(__int128 x) {
	if(x <= 9) {
		putchar(x + '0');
		return ;
	}
	write(x / 10);
	putchar(x % 10 + '0');
}
signed main() {
	cin >> k >> n;
	dp[0][0] = 1;
	for (int i = 1; i <= 54; i++)
		for (int j = 0; j <= 8; j++)
			for (int k = j; k <= i * 8; k++)
				dp[i][k] = (dp[i][k] + dp[i - 1][k - j]);
//	for (int i = 3; i <= 3; i++)
//		for (int j = 0; j <= 9; j++)
//			cout << i << " " << j << " " << (int)dp[i][j] << endl;
	tc = 1;
	for (int i = 1; i <= k; i++)
		tc = tc * 10;
	tc--;
//	cout << (int)dp[23][0] << endl;
	for (int i = (B / k + 2) * k - 1; i >= 0; i--) {
		while(1) {
			solve(i);
			//	cout << a[i] << " " << (int)all << endl;
			if(all >= n)
				break;
			n -= all;
			a[i]++;
		}
	}
	__int128 s = 0;
	for (int i = (B / k + 2) * k - 1, nw = 0; i >= 0; i--) {
		s = s * 10 + a[i];
		if(s >= tc)
			nw = 1;
		if(nw || s >= tc)
			cout << (int)(s / tc);
		s %= tc;
	}
	return 0;
}
posted @ 2025-10-28 23:03  LUlululu1616  阅读(38)  评论(0)    收藏  举报