【题目链接】
【思路要点】
- 记 f ( i ) f(i) f(i) 表示 i i i 最大的数位,则有 f ( i ) + 1 ≥ f ( i + 1 ) f(i)+1\geq f(i+1) f(i)+1≥f(i+1) ,即 f ( i ) ≤ f ( i − 1 ) + 1 f(i)\leq f(i-1)+1 f(i)≤f(i−1)+1 。
- 因此 i i i 能够到达的数 i − 1 i-1 i−1 必然可以到达,贪心地删去最大位是正确的。
- 将末尾数字看做自动机的节点,每一次我们将会让当前节点进行一次转移,该转移只与所在的自动机节点以及非末尾数字的最大值 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∼(i−1)×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; }