洛谷 P2657

题目大意

不含前导零且相邻两个数字之差至少为 \(2\) 的正整数被称为 \(windy\) 数。 \(windy\) 想知道,在 \(l\)\(r\) 之间,包括 \(l\)\(r\) ,总共有多少个 \(windy\) 数?

solution

有一个显而易见的结论

我们发现 \(ans_{l, r} = ans_{1. r} - ans_{1, l - 1}\)

那我们只需要处理 \(1 - r\) 的即可

那我们就可以来推我们的状态转移方程了

我们设 \(f_{i, j}\) 是长度 \(i\) , 最高位是 \(j\) 的个数

\(f_{i, j} += f_{i - 1, k}, | j - k | \geqslant 2\)

对于\(ans_{1, r}\) 我们可以采用以下策略 :

\(len\)\(r\) 的位数, \(a_{len}\)\(r\) 的每一位

  1. 对于所有长度小于 \(len\)\(f\) , \(res += f_{i, j}, i \in [1, len - 1], j \in [1, 9]\)
  2. 对于长度等于 \(len\)且最高位小于 \(a_{len}\)\(f\) , \(res += f_{len, j}, j \in [1, a_{len})\)
  3. 然后对于剩下的 \(len - 1\) 位, 我们继续执行 \(2\) 操作, 不过 \(j\in [0, a_i)\) (因为最高位已经不为0了)

答案就是 \(ans_{1, r} - ans_{1, l - 1}\)

Code:

#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>

#define int long long
#define rr register

#define MAXN 2100
#define MAXM 100010
#define inf 1e18

using namespace std;

const int mod = 998244353;

inline int read() {
	int s = 0, f = 0;
	char ch = getchar();
	while (!isdigit(ch)) {f |= ch == '-'; ch = getchar();}
	while (isdigit(ch)) {s = s * 10 + (ch ^ 48); ch = getchar();}
	return f ? -s : s;
}

int A, B;

int a[20];

int f[20][20];

inline void init() {
	for (rr int i = 0; i <= 9; i++) f[1][i] = 1;
	for (rr int i = 2; i <= 10; i++)
		for (rr int j = 0; j <= 9; j++)
			for (rr int k = 0; k <= 9; k++)
				if (abs(j - k) >= 2) f[i][j] += f[i - 1][k];
}

inline int work(int x) {
	memset(a, 0, sizeof a);
	int len = 0, ans = 0;
	while (x) {
		a[++len] = x % 10;
		x /= 10;
	}
	for (rr int i = 1; i <= len - 1; i++)
		for (rr int j = 1; j <= 9; j++)
			ans += f[i][j];
	for (rr int i = 1; i < a[len]; i++)
		ans += f[len][i];
	for (rr int i = len - 1; i >= 1; i--) {
		for (rr int j = 0; j < a[i]; j++)
			if (abs(j - a[i + 1]) >= 2) ans += f[i][j];
		if (abs(a[i + 1] -a[i]) < 2) break;
	}
	return ans;
}

signed main() {
	init();
	A = read();
	B = read();
	cout << work(B + 1) - work(A);
}
posted @ 2020-11-16 09:05  Aliemo  阅读(135)  评论(3编辑  收藏  举报