数位 DP

引入

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

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

给定 l,r,求 lr 中有多少 xf(x) 为真。

l,r1018

典型的例子是 windy 数

lr 中有多少个数,满足其不含前导零且相邻两个数字之差至少为 2

前置

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

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

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

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

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

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

  • g5(a)=g5(b)
  • g4(a)=g4(b)
  • g3(a)>g4(b)
  • g2(b)[0,9]
  • g1(b)[0,9]
  • g0(b)[0,9]

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

分析

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

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

接下来,我们令 y 表示一个比 x 小的数,ai 表示 x 的第 i 个十进制位,bi 表示 y 的第 i 个十进制位,cnt 表示其位数。例如 x=114514a16={4,1,5,4,1,1}

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

  • kicntai=bi
  • ak>bk
  • 1i<kbi[0,9]

那么这启发我们可以枚举使得 ak>bkk,然后计算答案。这样做我们就将 1x1 的数分成了 cnt 类。然后单独计算 x 是否合法即可。

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

首先枚举 k,再枚举 bk[0,ak1)。我们需要让相邻两个数字之差至少为 2。此时,我们已经知道了 bk+1=ak+1,所以此时可以之间判断枚举的 bk 是否合法,即是否满足 |ak+1bk|2

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

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

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

fi,j=0k9,|jk|2fi1,k

边界 f1,i=1

posted @   2huk  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示