AT_tdpc_number 数 解题报告
题目大意
求
数据范围:
思路
很明显的数位 dp。
这里采用了记忆化搜索来实现数位 dp。
记忆化搜索实现比较板子,不光写起来比较简单,而且容易扩展,所以建议大家都学习一下。
首先把上界
加上记忆化,设
然后在搜索过程中记一下当前数位和
注意到状态数为
注意!由于最后要
#include <cmath>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 10010, M = 110, mod = 1e9 + 7;
typedef long long ll;
char s[N];
int len, d;
int f[N][M];
vector<int> num;
//记忆化搜索
//pos 记录当前填到了哪一位,r 记录填完 pos + 1 到 len - 1 位后数位和模 d 的值
//limit 记录当前有没有顶上界,zero 记录当前是不是前导零
ll dfs(int pos, int r, bool limit, bool zero) {
//边界,若填完了就检查一下是否符合条件
if(pos < 0) return (r == 0);
//若不顶上界且没有前导零就记忆化,因为顶上界和前导零是特殊情况,满足条件的数可能和普通情况不同
if(!limit && !zero && f[pos][r] != -1) return f[pos][r];
//看一下当前这位需不需要顶上界,若前面填的数都是贴着上界的,这一位最多只能填到 num[pos],否则不受限
int mx = (limit ? num[pos] : 9);
ll res = 0;
//枚举第 pos 位填什么
for(int i = 0; i <= mx; i++)
//去填下一位,更新当前余数,看下一位是不是顶上界,看下一位是不是前导零
res = (res + dfs(pos - 1, (r + i) % d, limit && (i == num[pos]), zero && (!i))) % mod;
//若不顶上界且没有前导零就记忆化
if(!limit && !zero) f[pos][r] = res;
return res;
}
ll calc(char str[]) {
//把每一位抠出来,注意是倒过来的
for(int i = len - 1; i >= 0; i--)
num.push_back(s[i] - '0');
memset(f, -1, sizeof f);
return dfs(len - 1, 0, 1, 1);
}
int main() {
scanf("%s%d", s, &d);
len = strlen(s);
//因为我们搜索考虑了 0,最后要把一减去
printf("%lld", (calc(s) + mod - 1) % mod);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!