数位 dp

image-20241104214032198

void solve() {
int x, k;
cin >> x >> k;
int y = x + k;
x--;
auto cal = [](int x) {
vector<int> res;
while (x) {
res.emplace_back(x % 10);
x /= 10;
}
reverse(res.begin(), res.end());
return res;
};
auto a = cal(x);
auto b = cal(y);
int n = 0;
auto work = [&](vector<int> a) -> vector<int> {
if (a.empty()) {
// 直接不允许 0 作为有效值,所以 dp[0] 恒等于 0
// 这个规定非常重要,避免了前导 0 的存在!!!
return vector<int>(11, 0);
}
// dp: 0 代表没有贴着上界,1 代表贴着上界
vector dp(1 << 10, vector<int>(2));
// 由于不允许存在前导 0,因此从 1 开始初始化
for (int i = 1; i < a[0]; i++) {
dp[1 << i][0] = 1;
}
dp[1 << a[0]][1] = 1;
for (int i = 1; i < n; i++) {
auto ndp = dp;
dp.assign(1 << 10, vector<int>(2, 0));
// 由于不允许存在前导 0,因此从 1 开始初始化
// 这里是说,构建的数从第 i 位开始,
// 会带来一些额外的贡献,同时由于 i 已经不是最高位了,
// 所以 0 ~ 9 都是合法的!
for (int j = 1; j < 10; j++) {
dp[1 << j][0]++;
}
for (int j = 0; j < (1 << 10); j++) {
// 0
for (int k = 0; k < 10; k++) {
dp[j | 1 << k][0] += ndp[j][0];
}
// 1
for (int k = 0; k < a[i]; k++) {
dp[j | 1 << k][0] += ndp[j][1];
}
dp[j | 1 << a[i]][1] += ndp[j][1];
}
}
vector<int> cnt(11);
for (int j = 0; j < (1 << 10); j++) {
int k = 0;
while (j >> k & 1) k++;
assert(k <= 10);
cnt[k] += dp[j][0] + dp[j][1];
}
return cnt;
};
n = a.size();
auto low = work(a);
n = b.size();
auto cnt = work(b);
for (int i = 0; i <= 10; i++) {
cnt[i] -= low[i];
}
for (int i = 10; i >= 0; i--) {
if (cnt[i]) {
cout << i << ' ' << cnt[i] << "\n";
return;
}
}
}