正经题解|寻找特别之数
2024-03-25 10:16:06
发布于:浙江
74阅读
0回复
0点赞
题目解析
考虑 。
将数字 转化为字符串 并 ,定义 表示构造低位第 位及其之前数位的合法方案数,其余参数含义为:
- 表示前面选择的数字除以 的余数。
- 表示前面选择的数字是否存在 。若 表示存在 ,否则表示不存在 。
- 表示当前是否受到了 的约束(注意要构造的数字不能超过 )。若为真,则第 位填入的数字至多为 ,否则可以是 。如果在受到约束的情况下填了 ,那么后续填入的数字仍会受到 的约束。例如 那么 填的是 的话, 的这一位至多填 。
AC代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
constexpr int N = 18;
constexpr int M = 3;
namespace DigitDp {
string s;
int n;
i64 dp[N + 1][M][2];
void init() {
memset(dp, -1, sizeof dp);
}
i64 dfs(int i, int r, int one, int isLim) {
if (i < 0) return r == 0 and one;
if (!isLim and dp[i][r][one] != -1)
return dp[i][r][one];
i64 res = 0;
int hi = (isLim ? s[i] - '0' : 9);
for (int d = 0; d <= hi; ++d)
res += dfs(i-1, (r*10+d)%3, one|(d==1), isLim and d == hi);
if (isLim) return res;
return dp[i][r][one] = res;
}
i64 calc(i64 x) {
init();
s = to_string(x);
reverse(begin(s), end(s));
n = s.size();
return dfs(n - 1, 0, 0, true);
}
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
DigitDp::init();
int _; cin >> _;
while (_--) {
i64 l, r;
cin >> l >> r;
i64 lo = DigitDp::calc(l - 1);
i64 hi = DigitDp::calc(r);
cout << hi - lo << '\n';
}
return 0;
}
复杂度分析
采用记忆化搜索实现数位DP。
。
本题状态的个数为 ,令 ,计算单个状态的时间为 ,所以本题数位DP的时间复杂度为 。
本题 可以 与查询次数无关,总的时间复杂度为 。
若 无法复用,则时间复杂度为 。
这里空空如也
有帮助,赞一个