BZOJ1026 [SCOI2009]windy数
Description
windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,
在A和B之间,包括A和B,总共有多少个windy数?
Input
包含两个整数,A B。
Output
一个整数
Sample Input
【输入样例一】
1 10
【输入样例二】
25 50
1 10
【输入样例二】
25 50
Sample Output
【输出样例一】
9
【输出样例二】
20
9
【输出样例二】
20
HINT
【数据规模和约定】
100%的数据,满足 1 <= A <= B <= 2000000000 。
题解
裸数位dp。。。。不过这数据范围不大对劲啊,不应该是$10^{100000}$那种,然后对$10^9+7$取模吗。。。
首先将问题转化成1到N的windy数个数。
设N的十进制表示有n位,定义$f_{i,j}$为只考虑第n-1位(第0位至第n-1位,一共n位)到第i位时,小于N的末尾是j的windy数个数,那么有
$$f_{i,j} = [i<n-1\;and\;isWindyNumber(N_{i+1..n-1})\;and\;j<N_i\;and\;abs(N_{i+1}-j)\geq 2] + [i<n-1\;and\;j>0] + \sum_{0 < k < 10, \\ abs(k - j) \geq 2}f_{i+1,k}$$
其中第一项表示前面所有位都与N相同,第i位比N小,第二项表示一位数,第三项表示其它情况。
最后如果N是windy数那么再加一。
附代码:
#include <algorithm> #include <cstdio> int p[10]; int f[10][10]; inline bool check(int a, int b) { return std::abs(a - b) >= 2; } int count(int N) { if (!N) return 0; int n = 0; while (N) { p[n++] = N % 10; N /= 10; } p[n] = 0; bool ok = true; for (int j = 0; j < 10; ++j) f[n][j] = 0; for (int i = n - 1; ~i; --i) { for (int j = 0; j < 10; ++j) { f[i][j] = ok && (j < p[i]) && ((i == n - 1 && j) || check(p[i + 1], j)); if (i != n - 1 && j) ++f[i][j]; for (int k = 0; k < 10; ++k) if (check(k, j)) f[i][j] += f[i + 1][k]; } if (i < n - 1 && !check(p[i], p[i + 1])) ok = false; } int ans = ok; for (int i = 0; i < 10; ++i) ans += f[0][i]; return ans; } int main() { int A, B; scanf("%d%d", &A, &B); printf("%d\n", count(B) - count(A - 1)); return 0; }