「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; 
}
posted @ 2021-01-19 14:30  Luckyblock  阅读(83)  评论(0编辑  收藏  举报