[BZOJ1026] [洛谷P2657] [SCOI2009]windy数 数位DP

题目描述

windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,

在A和B之间,包括A和B,总共有多少个windy数?

输入格式

包含两个整数,A B。

输出格式

一个整数

输入输出样例

输入 #1

1 10

输出 #1

9

输入 #2

25 50

输出 #2

20

说明/提示

100%的数据,满足 \(1 <= A <= B <= 2000000000\)


题解

数位DP

\(f[i][j]\)表示 \(i\) 位数,最高位为 \(j\)的符合条件的数的个数。

则$ f[i][j]=\sum_{|j−k|≤2}f[i−1][k] $。

预处理\(f\)数组后进行数位DP。

先填充位数不满的,再由高位向低位将此位不满的加入答案。

且若当前位于上一位数产生冲突导致不会再有满足条件的数时,跳出循环。

转换询问区间为\([1, n)\)则更易简单处理。

code

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
LL f[50][20], bit[50], a, b;
inline int ab(int x) { return x > 0 ? x : -x; }
void init() {
    bit[0] = 1; bit[1] = 10;
    for(int i = 0;i <= 9;i ++) f[1][i] = 1;
    for(int i = 2;i <= 20;i ++) {
        bit[i] = bit[i-1] * 10;
        for(int j = 0;j <= 9;j ++) {
            for(int k = 0;k <= 9;k ++) {
                if(ab(j-k) >= 2) f[i][j] += f[i-1][k];
            }
        }
    }
}
LL calc(int x) {
    LL pos, di = 1, res = 0, last = -1;
    for(pos = 1;bit[pos] <= x;pos ++) {
        for(int j = 1;j <= 9;j ++) {
            res += f[pos][j];
        }
    }
    for( ; pos ;pos --) {
        int now = (x / bit[pos - 1]) % 10;
        for(int j = di;j < now;j ++) {
            if(ab(j-last) >= 2) res += f[pos][j];
        }
        if(ab(now-last) < 2) break;
        di = 0; last = now;
    }
    return res;
}
int main() {
    init();
    cin >> a >> b;
    cout << calc(b + 1) - calc(a) << endl;
    return 0;
}
posted @ 2019-08-13 05:59  Paranoid丶离殇  阅读(...)  评论(... 编辑 收藏