题解:P13085 [SCOI2009] windy 数(加强版)

· · 题解

好不容易蹭到一个题解。

前情回顾还有这个,于是就有了这题。

某个帖子已经提示我们了,要写数位 dp。

数位 dp 的思想是 dp 与前缀和,我们记 windy_i[1,i] 之间的 Windy 数,则 windy_{[l ,r]} = windy_r - windy_{l - 1}

然后我们进行 dp,来设 f_{pos ,lst} 表示到了第 pos 位,前一位值是 lst 的 Windy 数个数。我们从高位向低位 dp,这样方便我们后续做某些事情,下文展开。那么转移方程为 f_{pos ,lst} = \sum\limits_{i = 0}^9 [i - lst \ge 2]\times f_{pos + 1 ,i} i 表示枚举当前位数。

等一下,是不是有什么没有考虑?

是的,我们逐一攻破。

# include <bits/stdc++.h>

# define int long long
# define up(i ,x ,y) for (int i = x ; i <= y ; i ++)
# define dn(i ,x ,y) for (int i = x ; i >= y ; i --)
# define inf 1e14

using namespace std;

inline int read (){int s = 0 ; bool w = 0 ; char c = getchar () ; while (!isdigit (c)) {w |= (c == '-') ,c = getchar () ;} while (isdigit (c)){s = (s << 1) + (s << 3) + (c ^ 48) ; c = getchar ();}return w ? -s : s;}
inline void write (int x){if (x < 0) putchar ('-') ,x = -x; if (x > 9) write (x / 10) ; putchar (x % 10 | 48);}
inline void writesp (int x){write (x) ,putchar (' ');}
inline void writeln (int x){write (x) ,putchar ('\n');}

const int N = 25 ,mod = 1e9 + 7;
int f[25][10] ,a[25];
inline int dfs (int pos ,int lst ,bool leading ,bool limit){
//leading = 0 : 前导 0. 反之。
  if (!pos) return 1;
  if (leading && !limit && f[pos][lst] != -1) return f[pos][lst];//无前导零 + 不贴边界 + 记忆化.
  int lim = limit ? a[pos] : 9 ,res = 0;//lim 为选择边界。
  up (i ,0 ,lim)
    if (abs (i - lst) >= 2) res += dfs (pos - 1 ,
    (!leading && !i) ? -2 : i /*如果还能保持前导零那么 lst 为 -2,这样后面随便填,因为 0~9 - (-2) 都大于等于 2.*/,
    leading || i ,/*1.已经没有前导零了,2.这一位不为 0.*/
    limit && (i == lim));//是否贴着边界.
  if (!limit && leading) f[pos][lst] = res;//记忆化.
  return res;
} inline int solve (int x){
  int cnt = 0 ;
  memset (f ,-1 ,sizeof f);
  while (x) {
    a[++ cnt] = x % 10;
    x /= 10;
  } return dfs (cnt ,-2 ,0 ,1);
} signed main (){
  int l = read () ,r = read ();//writeln (solve (r));
  writeln (solve (r) - solve (l - 1));
  return 0 ;
}