1 条题解
-
0
思路与算法
把区间统计改成F(B)-F(A-1),F(t)为不超过t的正Windy数数量。
从最高位递归,记录剩余位位置pos、前一个有效数位previous,以及是否仍受上界约束tight。previous=10是尚未开始的哨兵。未开始且当前放0时仍保持10,不把补位0当作真实数位;否则只有第一位有效数字,或与前一位差的绝对值至少2时,才可转移。
全部位结束后,previous不为10才计一种,排除整数0。非tight状态按pos和previous记忆化。
正确性说明
构造过程中previous始终是已经写出的最后一个真实数位,或者表示尚未开始。每次加入有效数字时检查与上一真实数字的差,恰好满足Windy数对每对相邻位的要求;前导补0不会引入额外约束。
tight保证构造值不超过上界,结束时排除全0路径。每个正整数只有一个补齐到上界位数的数位序列,因此合法数恰计一次。故F准确,前缀差给出闭区间答案。
复杂度
十进制位数L≤10。缓存状态数O(L×11),每个枚举至多10个数字,时间O(L×11×10)。参考程序每次还清空15×11个缓存单元,含初始化为O(15×11+L×11×10)时间,空间O(15×11+L)。
易错点
不能把前导0视为上一位0,否则会错误排除许多一位数。条件是差的绝对值至少2,等于2允许。0不属于正Windy数;A=1需要F(0)=0。两组原站公开样例都已纳入评测,且需考虑端点相同的区间。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; using ll = long long; vector<int> digit; ll memo[15][11]; ll solve(int pos, int previous, bool tight) { if (pos < 0) return previous != 10; if (!tight && memo[pos][previous] != -1) return memo[pos][previous]; int cap = tight ? digit[pos] : 9; ll answer = 0; for (int d = 0; d <= cap; ++d) { if (previous == 10 && d == 0) answer += solve(pos - 1, 10, tight && d == cap); else if (previous == 10 || abs(previous - d) >= 2) answer += solve(pos - 1, d, tight && d == cap); } if (!tight) memo[pos][previous] = answer; return answer; } ll count(ll n) { if (n <= 0) return 0; digit.clear(); while (n) { digit.push_back(n % 10); n /= 10; } memset(memo, -1, sizeof(memo)); return solve(digit.size() - 1, 10, true); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); ll a, b; cin >> a >> b; cout << count(b) - count(a - 1) << '\n'; }
- 1