数位dp练习

[SCOI2009] windy 数

题目背景

windy 定义了一种 windy 数。

题目描述

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

输入格式

输入只有一行两个整数,分别表示 a a a b b b

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1

1 10

样例输出 #1

9

样例 #2

样例输入 #2

25 50

样例输出 #2

20

提示

数据规模与约定

对于全部的测试点,保证 1 ≤ a ≤ b ≤ 2 × 1 0 9 1 \leq a \leq b \leq 2 \times 10^9 1ab2×109

我们要学会画这个图
在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

const int N = 12;

int f[N][10];
int a[N];

// 前置处理
void init() {
	for (int i = 0; i < 10; i++) f[1][i] = 1;
	for (int i = 2; i < 12; i++) {
		for (int j = 0; j < 10; j++) {
			for (int k = 0; k < 10; k++) {
				if (abs(j - k) >= 2) f[i][j] += f[i - 1][k];   // 只有符合的才会加上来
			}
		}
	}

	//cout << "输出f" << endl;

}

int dp(int x) {
	if (!x) return 0;
	int cnt = 0;
	while (x)
	{
		a[++cnt] = x % 10, x /= 10;
	}
	int res = 0, last = -2;
	for (int i = cnt; i > 0; i--) {  // 从高位进行处理
		int now = a[i];
		for (int j = (i == cnt); j < now; j++) // 最高位要从1开始
		{
			if (abs(j - last) >= 2) res += f[i][j];
		}
		if (abs(now - last) < 2) break; // 如果不满足定义,就break,就没有分支了
		last = now;
		if (i == 1) res++;
	}
	// 如果答案有前导零
	for (int i = 1; i < cnt; i++) {
		for (int j = 1; j <= 9; j++) {
			res += f[i][j];
		}
	}

	

	return res;
}

int main() {
	init();
	int l, r;
	cin >> l >> r;
	cout << dp(r) - dp(l - 1);
	return 0;
}

[ZJOI2010] 数字计数

题目描述

给定两个正整数 a a a b b b,求在 [ a , b ] [a,b] [a,b] 中的所有整数中,每个数码(digit)各出现了多少次。

输入格式

仅包含一行两个整数 a , b a,b a,b,含义如上所述。

输出格式

包含一行十个整数,分别表示 0 ∼ 9 0\sim 9 09 [ a , b ] [a,b] [a,b] 中出现了多少次。

样例 #1

样例输入 #1

1 99

样例输出 #1

9 20 20 20 20 20 20 20 20 20

提示

数据规模与约定
  • 对于 30 % 30\% 30% 的数据,保证 a ≤ b ≤ 1 0 6 a\le b\le10^6 ab106
  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ a ≤ b ≤ 1 0 12 1\le a\le b\le 10^{12} 1ab1012
#include <bits/stdc++.h>
using namespace std;
const int N = 15;
typedef long long ll;
ll l, r, dp[N], mi[N];
ll ans1[N], ans2[N];
int a[N];

void solve(ll n, ll *ans) {
  ll tmp = n;
  int len = 0;
  while (n) a[++len] = n % 10, n /= 10;
  for (int i = len; i >= 1; --i) {
    for (int j = 0; j < 10; j++) ans[j] += dp[i - 1] * a[i];
    for (int j = 0; j < a[i]; j++) ans[j] += mi[i - 1];
    tmp -= mi[i - 1] * a[i], ans[a[i]] += tmp + 1;
    ans[0] -= mi[i - 1];
  }
}

int main() {
  scanf("%lld%lld", &l, &r);
  mi[0] = 1ll;
  for (int i = 1; i <= 13; ++i) {
    dp[i] = dp[i - 1] * 10 + mi[i - 1];
    mi[i] = 10ll * mi[i - 1];
  }
  solve(r, ans1), solve(l - 1, ans2);
  for (int i = 0; i < 10; ++i) printf("%lld ", ans1[i] - ans2[i]);
  return 0;
}

度的数量

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

数字游戏

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

const int N = 14;
int f[N][10];
int a[N];

void init() {
	for (int i = 0; i < 10; i++) {
		f[1][i] = 1;
	}
	for (int i = 2; i < N; i++) {  //前导零也被考虑进来了
		for (int j = 0; j < 10; j++) {
			for (int k = j; k < 10; k++) {
				f[i][j] += f[i - 1][j];
			}
		}
	}
}

int fun(int x) {
	if (!x) return 1;
	// 先特判
	int cnt = 0;
	while (x)
	{
		a[++cnt] = x % 10, x /= 10;
	}
	int res = 0,last = 0;
	for (int i = cnt; i > 0; i--) {
		// 从高位进行处理
		int now = a[i];
		for (int j = last; j < now; j++) {
			res += f[i][j];
		}
		if (now < last)  break; // 当前位置上就小于上面一位了
		last = now; // 开启右边的分支
		if (i == 1) res += 1;
	}
	return res;
}

int main() {
	int p,m;
	init();
	cin >> p;
	cout << fun(m)-fun(p-1);
	return 0;
}
  • 22
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wniuniu_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值