数位 DP

引入

一般数位 DP 的题目是这样的:

有一个返回值为 bool 类型的函数 \(f(x)\)。这个函数一般是跟 \(x\)数位有关的。

给定 \(l, r\),求 \(l \sim r\) 中有多少 \(x\)\(f(x)\) 为真。

\(l, r \le 10^{18}\)

典型的例子是 windy 数

\(l \sim r\) 中有多少个数,满足其不含前导零且相邻两个数字之差至少为 \(2\)

前置

我们回顾一下如何比较两个数 \(a, b\) 的大小。

下面定义 \(g_j(i)\) 表示 \(i\) 的第 \(j\) 个十进制位(从右往左数,从 \(0\) 开始)是多少。例如 \(g_{2}(114514) = 5\)

  • \(a = 114\)\(b = 99\)
    • \(g_2(a) = 1\)\(g_2(b) = 0\)。所以 \(a > b\)
    • 这告诉我们位数多的数大。
  • \(a = 919\)\(b = 114\)
    • \(g_2(a) = 9\)\(g_2(b) = 1\)。所以 \(a > b\)
    • 这告诉我们最高位大的数大。
  • \(a = 191\)\(b = 114\)
    • \(g_2(a) = g_2(b) = 1\),不能判断。
    • \(g_1(a) = 9\)\(g_1(b) = 1\)。所以 \(a > b\)
    • 这告诉我们在最高位相同的情况下,次高位大的数大。
  • \(a = 119\)\(b = 114\)
    • \(g_2(a) = g_2(b) = 1\),不能判断。
    • \(g_1(a) = g_1(b) = 1\),不能判断。
    • \(g_0(a) = 9\)\(g_0(b) = 4\)。所以 \(a > b\)
    • 这告诉我们在最高位、次高位相同的情况下,第三高的位大的数大。
  • 依次类推。

所以呢?这告诉我们这样一个事情:

  • 对于数 \(a\),如果一个数 \(b\)最前面几位\(a\) 相同,接下来的一位\(a\) 小,那么剩下的位不管是 \(0 \sim 9\) 都能保证 \(a > b\)

例如,在 \(a = 114514\)\(b = 113999\) 时,有这样几个关系:

  • \(g_5(a) = g_5(b)\)
  • \(g_4(a) = g_4(b)\)
  • \(g_3(a) > g_4(b)\)
  • \(g_2(b) \in [0, 9]\)
  • \(g_1(b) \in [0, 9]\)
  • \(g_0(b) \in [0, 9]\)

这个东西是很显然的,但很关键。

分析

回到题目。我们利用前缀和的思想,转化成求 \(f(x)\) 表示 \(1 \sim x\) 中有多少满足条件的数,也就是有多少比的小于等于 \(x\) 的数满足条件。那么答案为 \(f(r) - f(l - 1)\)

那么问题就来到了 \(f(x)\) 上。

接下来,我们令 \(y\) 表示一个比 \(x\) 小的数,\(a_i\) 表示 \(x\) 的第 \(i\) 个十进制位,\(b_i\) 表示 \(y\) 的第 \(i\) 个十进制位,\(cnt\) 表示其位数。例如 \(x = 114514\)\(a_{1 \sim 6} = \{4, 1, 5, 4, 1, 1\}\)

上面提到,如果 \(x > y\),那么一定存在一个整数 \(k \in [1, cnt]\),使得:

  • \(k \le i \le cnt\)\(a_i = b_i\)
  • \(a_k > b_k\)
  • \(1 \le i < k\)\(b_i \in [0, 9]\)

那么这启发我们可以枚举使得 \(a_k > b_k\)\(k\),然后计算答案。这样做我们就将 \(1 \sim x - 1\) 的数分成了 \(cnt\) 类。然后单独计算 \(x\) 是否合法即可。

接下来要做的事情需要根据题目分析。以 windy 数为例。

首先枚举 \(k\),再枚举 \(b_k \in [0, a_k - 1)\)。我们需要让相邻两个数字之差至少为 \(2\)。此时,我们已经知道了 \(b_{k + 1} = a_{k + 1}\),所以此时可以之间判断枚举的 \(b_k\) 是否合法,即是否满足 \(|a_{k + 1} - b_k| \ge 2\)

如果满足,那么问题就变成了:

有多少个 \(k\) 位数,其最高位为 \(b_k\),并且满足相邻两个数字之差至少为 \(2\)。其中 \(k\)\(b_k\) 是给定的。

这个可以直接预处理 DP。设 \(f_{i, j}\) 表示有多少个 \(i\) 位数的最高位为 \(j\)。那么转移:

\[f_{i, j} = \sum_{0 \le k \le 9, |j - k| \ge 2} f_{i - 1, k} \]

边界 \(f_{1, i} = 1\)

posted @ 2024-01-20 19:45  2huk  阅读(13)  评论(0编辑  收藏  举报