小米OJ - 找到第 N 个数字 II - 等差数列求和及其通项然后二分

版权声明:本文为博主原创文章,禁止转载。 https://blog.csdn.net/FlushHip/article/details/86644049

题目描述

链接Here

假如有一组字符串符合如下规律:

S 1 = 1 S 2 = 12 S 3 = 123 S 4 = 1234 S 9 = 123456789 S 10 = 12345678910 S 18 = 123456789101112131415161718 \begin{array}{} S_{1} =1\\ S_{2} =12\\ S_{3} =123\\ S_{4} =1234\\ \cdots \\ S_{9} =123456789\\ S_{10} =12345678910\\ \cdots \\ S_{18} =123456789101112131415161718 \end{array}

(对于 S n S_n 来说,将从 1 1 n n 的数字拼接到一起)

现在我们把所有的字符串拼接起来,组成一个无限长的字符串 S = 1121231234 12345678910111213 S = 1121231234 \cdots 12345678910111213 \cdots 你能找出该字符串的第 n n 位数字是多少吗?

输入

一个整数( 1 1 < 整数 < 1 0 15 10^{15} ),表示所求的位数是多少位

输出

一个整数,表示该位上的数字是多少

解析

其实就是要确定第N位数字所在的项是哪一项。这里分两层。

一层是 L e n ( S n ) Len(S_n) 的长度,一层是 L e n ( i = 1 n L e n ( S i ) ) Len(\sum_{i=1}^{n} Len(S_i)) 的长度,分别抽象成 f ( x ) , g ( x ) f(x), g(x) ,观察数字长度的特点就可以知道, f ( x ) f(x) 是个特殊的等差数列,而 g ( x ) g(x) 是特殊的等差数列的前 x x 项和。

公差依次是:

1 9 10 99 100 999 1 0 n 1 0 n + 1 1 1 2 3 n + 1 \begin{matrix} 1\sim 9 &amp; 10\sim 99 &amp; 100\sim 999 &amp; \cdots &amp; 10^{n} \sim 10^{n+1} -1\\ 1 &amp; 2 &amp; 3 &amp; \cdots &amp; n+1 \end{matrix}

依照这个就可以很容易算出 f ( x ) , g ( x ) f(x), g(x) ,然后利用输入的 n n 二分求上界(可以参考你真的理解二分的写法吗 - 二分写法详解)算出 g ( x ) &lt; n g(x) &lt; n 的最大 x x

利用这个 x x 算出 r e s = n g ( x ) res = n - g(x) ,然后利用输入的 r e s res 二分求上界算出 f ( y ) &lt; r e s f(y) &lt; res 的最大 y y

利用这个 y y 算出 r e s s = r e s f ( y ) ress = res - f(y) r e s s ress 表示整数 y + 1 y + 1 的第 r e s s ress 位数字,这就是答案了。

这样做应该是最快的解法了。

代码

#include <bits/stdc++.h>

typedef long long LL;

int main()
{
    auto calFG = [] (LL n) -> std::pair<LL, LL>
    {
        LL start = 0, d = 1, nines = 9, sum = 0, res = 0;
        for (; sum + nines < n; ++d, sum += nines, nines *= 10) {
            res += nines * start + nines * (nines - 1) / 2 * d;
            start += nines * d;
        }
        return { start + d * (n - sum), res + (n - sum + 1) * start + (n - sum + 1) * (n - sum) / 2 * d };
    };
    auto bFind = [] (LL n, const std::function<LL(int)> & func) -> int
    {
        int l = 0, r = 1000000;
        while (l < r) {
            int mid = l + (r - l + 1) / 2;
            if (func(mid) < n) {
                l = mid;
            } else {
                r = mid - 1;
            }
        }
        return l;
    };
    for (LL n; std::cin >> n; ) {
        int ii = bFind(n, std::bind([&] (int m) -> LL
        {
            return calFG(m).second;
        }, std::placeholders::_1));
        LL res = n - calFG(ii).second;
        int i = bFind(res, std::bind([&] (int m) -> LL
        {
            return calFG(m).first;
        }, std::placeholders::_1));
        LL ress = res - calFG(i).first;
        assert(ress);
        std::cout << std::to_string(i + 1).at(ress - 1) << std::endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/FlushHip/article/details/86644049