Problem 1
原题:
简单分析一下题目,大概是这样的:
给定T组数据,对于每一组数据 $T_{i}$ ,你需要找出 $[1,T_{i}]$ 内的所有自然数中,链式四舍五入和直接四舍五入结果不同的数的个数。
四舍五入的要求:
四舍五入到大于 $T_{i}$ 的最小的10的整数次幂 (设为 $10^b$),例如 12372 会被舍入到 $10^5 (100,000)$ 的级别,即 $b=5$.
实际上,最终的四舍五入结果要么是 $10^b$,要么是0.
其中,两种四舍五入的方法解释如下:
- 链式四舍五入: 先舍入到 $10^1$,再舍入到 $10^2$,以此类推直到 $10^b$.
- 直接四舍五入:直接舍入到 $10^b$.
我看完题,就决定直接找规律了……
仔细想一想,如果链式四舍五入和直接四舍五入有区别,那么只能是链式四舍五入使得最高位的4变为了5,进而在最后一步舍入时变为 $10^b$ 而不是0. (因为此时在直接四舍五入方法下,由于最高位是4,因而最终舍入结果为0)
那么,什么样的数字满足这个条件呢?
我从简单的三位数、四位数入手(这也是样例中给出的数据处在的范围),
发现在所有的三位数中,445~499 都会使得两种方法舍入结果不同。
而对于四位数,则是 4445~4999.
因此,总结出规律:在 $n$ 位数中,舍入结果不同的数字区间始于(包含端点):
$$ 4 \times \left( \sum_{i=1}^{n-1} 10^i \right) + 5 $$
结束于(包含端点):
$$ 4 \times 10^{n-1} + 9 \times \left( \sum_{i=0}^{n-2} 10^i \right) $$
也就是说,对于给定的 $T_{i}$,我们只需要统计位数小于 $T_{i}$ 的所有数中,满足条件的数的个数,然后计算和 $T_{i}$ 相同位数的数。将两者加和,即是答案。这个处理流程的时间复杂度为常数 $O(1)$.
总共 $T$ 组数据,总时间复杂度为 $O(T)$.
实例
比如,给定数字 4567,我们发现它是四位数,那我们就先统计一位数、两位数和三位数中,满足条件(舍入结果不同)的数的个数,
发现:
- 一位数中没有这样的数,个数为 $0$.
- 两位数中45~49满足条件,个数为 $$ \begin{aligned} N &= 49-45+1 \\ &= 49-(45-1) \\ &= 49-44 \\ &= 5 \end{aligned} $$
- 三位数中445~499满足条件,个数为 $$ \begin{aligned} N &= 499-445+1 \\ &= 499-(445-1) \\ &= 499-444 \\ &= 55 \end{aligned} $$
对于四位数,由于 $ 4567 < 4999 $,我们只应计算 $[4445, 4567]$ 这个区间,个数为
$$ \begin{aligned} N &= 4567-4445+1 \\ &= 4567-(4445-1) \\ &= 4567-4444 \\ &= 123 \end{aligned} $$
因此,答案为 $0+5+56+123=184$.
照着例子设计程序
从上面的例子中,我们其实可以发现:
一位数、两位数、三位数……$N$位数中,满足条件的数的个数是固定的,
- 一位数有 0 个
- 两位数有 5 个
- 三位数有 55 个
- 四位数有 555 个
- 五位数有 5555 个
- 六位数有 55555 个
- …
因此,我们只需要预处理出这些数,把它预先打表成一个前缀和的常数数组,那么我们在计算位数小于 $T_{i}$ 的数中满足条件的数的个数时,直接查表即可。
这个常数表就是这样:
# accu stands for accumulate
accu = [0,0,0,5,60,615,6170,61725,617280,6172835,61728390,617283945]
索引下标为当前数字的位数,对应的数组元素为累计到当前位数-1
的数中,满足条件的数的个数。
剩下就没什么难度了。 我推荐用Python写,因为对于类似 $4 \times \left( \sum_{i=0}^{n-2} 10^i \right) + 5$ 这种数字(前面n-1位是4,个位数是5)的构建, Python直接用字符串拼接再转成 int 类型即可, 统计位数什么的也可以直接转成 str 然后统计字符串长度, 相当方便。
参考代码如下:
accu = [0,0,0,5,60,615,6170,61725,617280,6172835,61728390,617283945]
def solve():
# 输入数字
inp = input()
# 统计位数
digits = len(inp)
# 获得位数比 inp 小的数字中满足条件的数的个数
ld = accu[digits]
# 计算和 inp 相同位数的数中满足条件的数的个数
curr = max(0, min(int('4' + '9'*(digits - 1)), int(inp)) - int('4' * digits))
# 输出答案,结果即为两者之和
print(ld+curr)
T = int(input())
for i in range(T):
solve()