首页 > 代码库 > BZOJ1026 [SCOI2009]windy数

BZOJ1026 [SCOI2009]windy数

Description

  windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,
在A和B之间,包括A和B,总共有多少个windy数?

Input

  包含两个整数,A B。

Output

  一个整数

Sample Input

【输入样例一】
1 10
【输入样例二】
25 50

Sample Output

【输出样例一】
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;
}

  

BZOJ1026 [SCOI2009]windy数