1024 第 2 场算法双周赛 神奇数【算法赛】
时间:2023.11.1
题目地址: 神奇数【算法赛】
题目分析
数位dp,可套模板,就是将最后一位固定枚举(1-9),然后求前面所有可能的和。
有个推荐的模板:数位dp万能模板
详见代码
代码
from functools import lru_cache
def deal(s: str):
n = len(s)
ans = 0
for last in range(1, 10): # 固定最后一位只能为1~9
# i 表示当前数的位置
# total 存当前位置时的所有位数之和
# is_limit 就是判断当前位的上限,比如说100-123,当第一位为1时第二位[0,1,2]中选最高为2。当第一位为零时,第二位无限制0-9.
# is_num 预防前导零,也就是前一位有数字,那么后一位就可以从0开始;不然得从1开始,因为前一位没填数字,这一位就变成了第一位。
@lru_cache(None) # 记忆化
def f(i, total, is_limit, is_num):
if i == n - 1: # i到最后一位了就是last,可以返回给出结果
if not is_num or (is_limit and last > int(s[i])): # is_num为False, 那么没数字取模,或者此时的last超过了给出的s的最后一位,也不合法。
return 0
return total % last == 0 # 判断为结果的条件 True==1.
res = 0
if not is_num:
res += f(i+1, total, False, False)
low = 0 if is_num else 1 # 前一位有数字,那么后一位就可以从0开始;不然得从1开始,因为前一位没填数字,这一位就变成了第一位。
up = int(s[i]) if is_limit else 9 # 比如说100-123,当第一位为1时第二位[0,1,2]中选最高为2。
for d in range(low, up+1):
res += f(i+1, (total+d) % last, is_limit and d == up, True)
res %= mod
return res
ans += f(0, 0, True, False)
ans %= mod
f.cache_clear()
return ans
l = input()
r = input()
mod = 998244353
print((deal(r) - deal(str(int(l) - 1)))%mod) # 要记得取模