这题做了半天。
美丽的数的定义是“可以被自己的所有非零数位整除的数”。
做数位DP,想的就是每个状态需要什么参量来表示。注意到这个定义的限制并不在选取每个数位的时候,而是在整个数字选完之后才能做一个整体判断。
很自然地,首先想到需要知道我们已经在$2, 3, \dots, 9$中选了哪些数字,这可以用一个$7$位二进制数来表示。这个想法虽然正确,但在本题中时间效率不够。能够观察到,假如我们选了$9$而没有选$3$,这种情况在最后需要的判断,和既选了$9$也选了$3$是等价的。这不免让我们意识到,可以拿我们已选的所有数位在$2, 3, 5, 7$的最高次幂分别作为一个状态参量,它一共有$4\times3\times2\times2=48$种可能取值,比一个$7$位二进制数来得小很多。这样做其实是可以的,但写起来稍微麻烦一点。
我们依然可以更进一步:能够被我们的所有数位整除,就等价于能够被我们的所有数位的最小公倍数整除。虽然这个最小公倍数最大能达到$2520$,但根据刚才的论述,它只有$48$个可能的取值,对应的是$2520$的所有因数,简单离散化即可。 我们需要判断的是整个数能否被所有数位的最小公倍数整除。最理想的做法是记录状态参量包括“每一步选完后的数前缀”模“我们所选的所有数位的最小公倍数”的值。但这是不可能的,因为在选取的每一步,我们无法预知后面的步骤会选择什么,也就无法预知这个最小公倍数是几。但所幸其实只要是这个最小公倍数的倍数作为模数,我们都可以用余数来判断原数对最小公倍数的整除性。因此最后我们选取的模数相等于是“所有最小公倍数的最小公倍数”,不难发现这个虚张声势的说法等价于$2, 3, \dots, 9$的最小公倍数$2520$。
理论上这道题就做完了。但是我又被卡时间了。这时候我意识到数位DP未必非要走套路:先求小于等于$n$的“满足条件的数”,再拿区间两个端点求出的值减一下——其实可以搜索的时候同时卡两个端点,然后一遍就搜完了。做法就是不仅记录“已选择的所有数中是否均和右端点对应数位相等”,也记录“已选择的所有数中是否均和左端点对应数位相等”,这两个状态分别影响每一步能选择的数的上界和下界。这么做的好处在于,由此搜索的复杂度就只和区间长度有关,和左右端点数的大小本身无关了。换言之,对于原先左右端点都很大、但区间长度未必大的数据,这样做显然能缩短运行时间;对于其他数据,也不会明显劣于之前的算法(未严格证明)。
1 |
|