1 条题解
-
0
思路与算法
用F(x)统计0至x中不含数码4且不含连续62的整数。状态记录位置、上一位是否为6、tight。枚举下一位时跳过4,上一位是6时再跳过2;其余转移更新上一位是否为6。区间答案为F(m)-F(n-1),00作为结束标记不处理。
正确性说明
被跳过的数位选择恰好是两类非法情况:当前位4,或上一位6且当前位2。状态完整保留判断新禁串所需的前一位信息,不会把不相邻的6和2误判。合法数与逐位路径一一对应,上界限制准确,故F正确。前导0不会制造4或62,0在区间差中相消,得到所求正号码数量。
复杂度
每个前缀查询最多7位,位置乘2种上一位状态,每状态至多10次转移,计算O(L210),另加固定11*2=22槽的缓存初始化。空间O(22+L),包括文本和递归栈。每组两次查询,按00结束,文件总开销与组数线性。
易错点
含4一律非法,62必须是相邻顺序;61152合法。不能把出现6或出现2本身当成非法。范围严格小于10^7,最大9999999已测试;00只结束输入。独立答案对全部1至9999999逐个检查十进制文本并建立前缀统计,覆盖完整数值域。
参考代码(C++11)
#include <cstring> #include <iostream> #include <string> using namespace std; string digits; long long memo[11][2]; long long visit(int pos, bool previousSix, bool tight) { if (pos == (int)digits.size()) return 1; if (!tight && memo[pos][previousSix] >= 0) return memo[pos][previousSix]; int upper = tight ? digits[pos] - '0' : 9; long long ans = 0; for (int d = 0; d <= upper; ++d) if (d != 4 && !(previousSix && d == 2)) ans += visit(pos + 1, d == 6, tight && d == upper); if (!tight) memo[pos][previousSix] = ans; return ans; } long long count(long long x) { if (x < 0) return 0; digits = to_string(x); memset(memo, -1, sizeof(memo)); return visit(0, false, true); } int main() { long long a, b; while (cin >> a >> b) { if (a == 0 && b == 0) break; cout << count(b) - count(a - 1) << '\n'; } }
- 1