[ARC052D] 9

这题和前面的这题很像(其实是一起找的)。

简要题意

\(1 \leq N \leq M\) 的整数 \(N\) 中,满足 \(N\) 除以 \(K\) 的余数与 \(N\) 的十进制表示各位数字之和除以 \(K\) 的余数相等的 \(N\) 的个数是多少?\(M,K \leq 10^{10}\)

解法

由于这回的 \(K\) 还是非常大,根本不可能放入状态中,我们决定先研究数位 dp 可以解决多少的数据。计算发现大约是 \(10^4\)。于是我们研究 \(10^4 < K \leq 10^{10}\) 该怎么求。由于 \(K\) 很大,我们可以枚举数字和是多少,然后根据数字和来每枚举数,只需 \(O(\frac{90m}{k} \times 10)\) 大约是 \(9\times 10^8\)卡卡就过了

然后再看看数位 dp 具体怎么实现。
我们设 \(dp_{k,s,t,lim}\) 表示考虑到第 \(k\) 位,数的大小 \(mod\ K = s\),数的数字和 \(mod\ K = t\),是否受到上限限制 \(lim\) 的数的个数。
转移就非常简单,直接在代码里看吧。

Code:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e4 + 10, Lim = 1e4;
ll K, m, dp[12][N][91], num[15];
ll dfs(int pos, int s, int t, bool lim) {
	if(pos == 0) return s == t;
	ll g = dp[pos][s][t];
	if(!lim && g != -1) return g;
	g = 0;
	int up = lim ? num[pos] : 9;
	for(int i = 0; i <= up; i++) {
		g += dfs(pos - 1, (s * 10 + i) % K, (t + i) % K, lim && i == up);	
	}
	if(!lim) dp[pos][s][t] = g;
	return g;
}
ll work(ll x) {
	memset(dp, -1, sizeof dp);
	int cnt = 0;
	while(x) num[++cnt] = x % 10, x /= 10;
	return dfs(cnt, 0, 0, 1) - 1;
}
int main() {
	scanf("%lld%lld", &K, &m);
	if(K <= Lim) printf("%lld\n", work(m)), exit(0);
	int ans = 0;
	for(int i = 0; i <= 90; i++) {
		for(ll j = i; j <= m; j += K) {
			ll sum = 0, x = j;
			while(x) sum += x % 10, x /= 10;
			if(sum % K == i % K) ans++;
		}
	}
	printf("%lld\n", ans - 1);
	return 0;
}
posted @ 2026-07-24 14:30  OIerYang  阅读(8)  评论(0)    收藏  举报