「AHOI2009」同类分布
知识点:数位 DP
原题面:Luogu
简述
给定两个正整数 \(a\) 和 \(b\),求在 \([a,b]\) 中的所有整数中,各位数之和能整除原数的数的个数。
\(1\le a\le b\le 10^{18}\)。
3S,512MB。
分析
考虑到各位数之和与原数在 dfs 中都是变量,不易检验合法性。但发现各位数之和不大于 \(9\times 12\),考虑先枚举各位数之和,再在 dfs 时维护前缀的余数,以检查是否合法。
同样设 Dfs(int now_, int sum_, int p_, bool zero_, bool lim_, int val_),其中 \(\operatorname{sum}\) 为前缀的各数位之和,\(p\) 为原数模 \(\operatorname{val}\) 的余数。
边界是搜索到第 \(\operatorname{length}+1\) 位,此时返回 \([\operatorname{sum}=\operatorname{val} \land \, p = 0]\)。
对数位和和余数简单记忆化即可,总复杂度 \(O(2\cdot10^2\log_{10}^3(n))\) 级别。
代码
//知识点:数位 DP
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <vector>
#define LL long long
const int kN = 20;
//=============================================================
int numlth;
LL f[kN][9 * kN][9 * kN];
std::vector <int> num;
//=============================================================
inline LL read() {
LL f = 1, w = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Chkmax(int &fir, int sec) {
if (sec > fir) fir = sec;
}
void Chkmin(int &fir, int sec) {
if (sec < fir) fir = sec;
}
LL Dfs(int now_, int sum_, int p_, bool zero_, bool lim_, int val_) {
if (now_ > numlth) return (sum_ == val_ && !p_);
if (!lim_ && f[now_][sum_][p_] != -1) return f[now_][sum_][p_];
LL ret = 0;
for (int i = 0, up = lim_ ? num[now_] : 9; i <= up; ++ i) {
if (zero_ && !i) ret += Dfs(now_ + 1, sum_, 10 * p_ % val_, true, lim_ && i == up, val_);
else ret += Dfs(now_ + 1, sum_ + i, (10 * p_ + i) % val_, false, lim_ && i == up, val_);
}
if (!zero_ && !lim_) f[now_][sum_][p_] = ret;
return ret;
}
LL Calc(LL val_) {
num.clear();
num.push_back(0);
for (LL tmp = val_; tmp; tmp /= 10) num.push_back(tmp % 10);
for (int i = 1, j = numlth = num.size() - 1; i < j; ++ i, -- j) {
std::swap(num[i], num[j]);
}
LL ret = 0;
for (int i = 1; i <= 9 * numlth; ++ i) {
memset(f, -1, sizeof (f));
ret += Dfs(1, 0, 0, true, true, i);
}
// printf("%lld %lld\n", val_, ret);
return ret;
}
//=============================================================
int main() {
LL a = read(), b = read();
printf("%lld\n", Calc(b) - Calc(a - 1));
return 0;
}
作者@Luckyblock,转载请声明出处。

浙公网安备 33010602011771号