1. 程式人生 > >BZOJ1026 [SCOI2009]windy數

BZOJ1026 [SCOI2009]windy數

pan cpp 所有 scanf return -- -1 || !=

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數