AtomAlpaca
逝去的从容逝去,重温的依然重温,在沧桑的枝叶间,折取一朵明媚,簪进岁月肌里,让它疼痛又甜蜜,让它流去又流回。 ——汪曾祺《人间草木》
这道题目要求我们做这样几件事:
判断是否存在 使得 ;
如果存在,求出 的最小值;
如果不存在,输出 .
通过分析 字典序 的定义,我们知道:
这是显然的, 因为随着 的增大, 字典序前面的数字绝对不会跑到它后面去. 对于不是 的形式的数字, 它前面的数字显然会可能增加. 这就意味着, 一定时, 随着 的增大, 单调递增.
由于 必须要出现在字典序列中, 的最小值为 . 此时也最小. 因此我们知道:
如果 , 那么一定不存在 使 ;
如果 , 那么所求 .
对于形如 的 , 显然 . 但对于一般数字, 我们首先需要设法求
如果我们求出在 前的数字的数量,加 便是 . 我们从位数从小到大的角度讨论在其字典序前的数字.
根据字典序的定义, 对于位数为 的数字 , 我们只需要与 的前 位进行比较. 我们预先处理出 的前 位 , 此时不难发现当且仅当 时, 在 前, 或 就是 本身. 又考虑到任意数字开头不为零, 因此, 在所有位数为 的数字中, 排在 前或与 相等的有 个.
因此我们得到: 其中 是 的位数.
注意这里其实算进来了一个等于 的数, 但是因为最后要加 , 所以二者可相互抵消.
当 时, 我们还要求出 令 . 考虑到对于任意 位数 , 若 , 一定排在 的字典序后, 因此我们直接讨论 位数.
我们发现,所有小于 的 位数, 在后面加上一个数字后, 得到的数字的字典序一定还在 的字典序之后,因此每增加一位, 字典序前的数字增加的量就等于上一位数 字典序前的数字增加的量的 倍. 由此我们得到, 将序列增加到 位数时, 字典序前的数字会增加 个.
令 , 我们不断枚举 ,让 每次减 , 当第 次减去后 时, 记录 及当时的 , 则答案为
这是因为, 当枚举到 时, 我们距离目标 还有 个数字, 这时我们加上这 个 位数, 在减去与 相等的那个数, 我们就能得到答案.
#include <iostream>
using std::cin;
using std::cout;
long long k, m, min, now, len;
long long power[20], nums[20];
int main()
{
cin >> k >> m;
power[0] = 1;
for (int i = 1; i <= 20; ++i)
{
power[i] = power[i - 1] * 10;
}
for (int i = 0; i <= 20; ++i)
{
if (k == power[i] and m != i + 1)
{
cout << 0;
return 0;
}
}
std::string str;
str = std::to_string(k);
len = str.length();
for (int i = 1; i < len; ++i)
{
nums[i] = k / power[(len - i)];
}
nums[0] = 0;
nums[len] = k;
for (int i = 1; i < 11; ++i)
{
if (k < power[i - 1])
{
break;
}
min += nums[i] - power[i - 1] + 1;
}
if (min > m)
{
cout << 0;
}
else if (min == m)
{
cout << k;
}
else
{
m -= min;
long long ans = power[len];
int i = 1;
while (true)
{
long long tmp = k * power[i] - power[len + i - 1];
if (m > tmp)
{
m -= tmp;
ans *= 10;
++i;
}
else
{
break;
}
}
ans += m - 1;
cout << ans;
}
return 0;
}