【BZOJ4230】倒计时

本文介绍了一种使用数位DP与自动机优化解决特定类型问题的方法,通过构造自动机节点并利用转移矩阵,实现了从当前节点到下一节点的有效转移,大幅提升了问题求解的效率。文章详细阐述了算法思路,包括贪心策略的选择、状态定义、转移操作及时间复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目链接】

【思路要点】

  • f ( i ) f(i) f(i) 表示 i i i 最大的数位,则有 f ( i ) + 1 ≥ f ( i + 1 ) f(i)+1\geq f(i+1) f(i)+1f(i+1) ,即 f ( i ) ≤ f ( i − 1 ) + 1 f(i)\leq f(i-1)+1 f(i)f(i1)+1
  • 因此 i i i 能够到达的数 i − 1 i-1 i1 必然可以到达,贪心地删去最大位是正确的。
  • 将末尾数字看做自动机的节点,每一次我们将会让当前节点进行一次转移,该转移只与所在的自动机节点以及非末尾数字的最大值 M a x Max Max 有关,可以描述为自动机上末尾数字的一个标为 M a x Max Max 的出边。
  • 计算 t r a n s i , j , k trans_{i,j,k} transi,j,k 表示从自动机的节点 k k k 开始,进行 i × 1 0 j ∼ ( i − 1 ) × 1 0 j + 1 i\times10^j\sim(i-1)\times10^j+1 i×10j(i1)×10j+1 中所有的数对应的转移将到达的节点,以及经过的转移次数即可加速转移。
  • 时间复杂度 O ( σ 3 L o g N ) O(\sigma^3 LogN) O(σ3LogN) ,其中 σ \sigma σ 表示进制大小,有 σ = 10 \sigma=10 σ=10 ,单次询问复杂度可以做到 O ( σ L o g N ) O(\sigma LogN) O(σLogN)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 20;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
// Depth, Max, Pos
pair <int, ll> trans[MAXN][10][10];
pair <int, ll> operator + (pair <int, ll> a, pair <int, ll> b) {
	a.first = b.first;
	a.second += b.second;
	return a;
}
int Max(ll n) {
	ll ans = 0;
	while (n != 0) {
		chkmax(ans, n % 10);
		n /= 10;
	}
	return ans;
}
ll query(ll n, pair <int, ll> now) {
	while (n >= 0) {
		ll bit = 1; int layer = 0;
		while (n / bit % 10 == 9) {
			bit *= 10;
			layer++;
		}
		now = now + trans[layer][Max(n / bit)][now.first];
		n -= bit;
	}
	return now.second;
}
int main() {
	ll n; read(n);
	for (int i = 0; i <= 9; i++)
	for (int j = 0; j <= 9; j++) {
		if (i == 0) {
			trans[0][i][j] = make_pair(0, j != 0);
			continue;
		}
		if (j >= i) trans[0][i][j] = make_pair(10 - i, 2);
		else trans[0][i][j] = make_pair(10 + j - i, 1);
	}
	for (int p = 1; p < MAXN; p++) {
		for (int i = 0; i <= 9; i++)
		for (int j = 0; j <= 9; j++) {
			pair <int, ll> now = make_pair(j, 0);
			for (int k = 9; k >= 0; k--)
				now = now + trans[p - 1][max(i, k)][now.first];
			trans[p][i][j] = now;
		}
	}
	writeln(query(n / 10, make_pair(n % 10, 0)));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值