CF2132D

Solution

对于第 $k$ 个数字,我们首先确定是多少位数和哪一个数。
然后把数位比它小的数字加起来,
再算比它小但是数位相同的数字。
最后算它自己。
计算位数和的原理就是先算有多少个,比如 $100\rarr 999$ 也就是三位数有 $9\times100$ 个,然后每个数字 $1\rarr9$ 会出现 $3\times 9$ 次,总和就是 $3\times45$,这里的 $45$ 就是数字 $1\rarr9$ 的和,下面代码里也是这个意思。

Code

using LL=long long;
LL calc(LL n) {//计算 n 位数的和
    if (n <= 0) return 0;
    if (n < 10) return n * (n + 1) / 2; //小于10直接输出,为什么去看后面
    LL base = 1;
    int len = 0;
    while (base * 10 <= n) {//确定位数
        base *= 10;
        len++;
    }
    LL first = n / base;//首位
    LL  r = n % base;//剩下的数字
    LL len = (LL)len * 45 * (base / 10);//其实是 len*9/2*base但是因为base是10的倍数所以可以这样防止小数
    LL p1 = (LL)first * (first - 1) / 2 * base + first * len;//
    LL p2 = first * ( r + 1) + calc( r);//答案就是当前位的加上小一位的
    return p1 + p2;
}
void solve(){
    LL k;
    cin >> k;
    int d = 1;
    LL base = 1;
    while (k > 9 * base * d) {
        k -= 9 * base * d;
        d++;
        base *= 10;
    }
    LL  st = base;//确定位数
    LL index = (k - 1) / d;//首位
    LL  r = (k - 1) % d + 1;//包含第 k 位的数剩下的位数
    LL num =  st + index;//包含第 k 位剩下的数字
    LL p1 = calc( st - 1);//计算位数小的那个部分
    LL p2 = calc( st + index - 1) - calc( st - 1);//和自己数位相同的减去大的部分
    string s = to_string(num);//快速计算位数和
    LL p3 = 0;
    for (int i = 0; i <  r; i++) {
        p3 += s[i] - '0';
    }
    cout << p1 + p2 + p3 << endl;//把三个部分加起来
}